@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/http.js CHANGED
@@ -68,6 +68,23 @@ var VideoPreset = z.enum([
68
68
  ]);
69
69
  var VoiceId = z.string();
70
70
  var _VIDEO_MODEL_SET = new Set(VideoModel.options);
71
+ var BROLL_VIDEO_MODES = new Set(VideoModel.options);
72
+ var PersistedVideoMode = z.enum([
73
+ ...VideoModel.options,
74
+ "avatar_talking",
75
+ "hybrid",
76
+ "sora"
77
+ ]);
78
+ var PersistedVideoModel = z.enum([
79
+ ...VideoModel.options,
80
+ "sora",
81
+ "infinitalk"
82
+ ]);
83
+ var PLAN_VIDEO_MODES = [
84
+ ...VideoModel.options,
85
+ "avatar_talking",
86
+ "hybrid"
87
+ ];
71
88
 
72
89
  // ../../packages/types/src/video-creation.ts
73
90
  import { z as z3 } from "zod";
@@ -1038,40 +1055,206 @@ var VideoTier = z8.preprocess((v) => v === "grok" ? "single_call" : v, z8.enum([
1038
1055
  "single_call"
1039
1056
  ]));
1040
1057
  var DEFAULT_VIDEO_TIER = "single_call";
1058
+ var WritableVideoTier = VideoTier.refine((tier) => tier !== "legacy", {
1059
+ message: "VIDEO_TIER_RETIRED"
1060
+ });
1041
1061
 
1042
- // ../../packages/types/src/image-provider.ts
1043
- var GPT_IMAGE_2_RATIOS_BLOCKED_ABOVE_1K = [
1044
- "4:5",
1045
- "5:4"
1062
+ // ../../packages/types/src/image-models.ts
1063
+ var IMAGE_RESOLUTIONS = [
1064
+ "1k",
1065
+ "2k",
1066
+ "4k"
1046
1067
  ];
1047
- var GPT_IMAGE_2_5_RATIOS_BLOCKED_ABOVE_1K = [
1048
- "4:5",
1049
- "5:4",
1050
- "27:16",
1051
- "16:27",
1052
- "9:8",
1053
- "8:9"
1068
+ var GPT_IMAGE_2_5_TOKENS = {
1069
+ "1k": "gpt-image-2-5:1k",
1070
+ "2k": "gpt-image-2-5:2k",
1071
+ "4k": "gpt-image-2-5:4k"
1072
+ };
1073
+ var GPT_IMAGE_2_TOKENS = {
1074
+ "1k": "gpt-image-2:1k",
1075
+ "2k": "gpt-image-2:2k",
1076
+ "4k": "gpt-image-2:4k"
1077
+ };
1078
+ var GPT_IMAGE_2_5 = {
1079
+ id: "gpt-image-2.5",
1080
+ status: "active",
1081
+ renderedBy: "gpt-image-2.5",
1082
+ tokens: GPT_IMAGE_2_5_TOKENS,
1083
+ kie: {
1084
+ imageToImage: [
1085
+ "gpt-image-2-5-sunburst-image-to-image",
1086
+ "gpt-image-2-5-flare-image-to-image"
1087
+ ],
1088
+ textToImage: [
1089
+ "gpt-image-2-5-sunburst-text-to-image",
1090
+ "gpt-image-2-5-flare-text-to-image"
1091
+ ],
1092
+ shorthands: [
1093
+ "gpt-image-2-5"
1094
+ ]
1095
+ },
1096
+ fal: {
1097
+ edit: "openai/gpt-image-2.5/sunburst/edit",
1098
+ textToImage: "openai/gpt-image-2.5/sunburst/text-to-image",
1099
+ quality: {
1100
+ "1k": "low",
1101
+ "2k": "medium",
1102
+ "4k": "high"
1103
+ },
1104
+ ourCostCents: {
1105
+ "1k": 1.5,
1106
+ "2k": 6.1,
1107
+ "4k": 17.8
1108
+ }
1109
+ },
1110
+ // Docs KIE de gpt-image-2-5 (sunburst/flare): 27:16, 16:27, 9:8, 8:9 solo 1K;
1111
+ // 4:5 y 5:4 también bloqueados por encima de 1K.
1112
+ ratiosBlockedAbove1k: [
1113
+ "4:5",
1114
+ "5:4",
1115
+ "27:16",
1116
+ "16:27",
1117
+ "9:8",
1118
+ "8:9"
1119
+ ],
1120
+ ledgerSuffix: {
1121
+ "1k": "gpt-image-2",
1122
+ "2k": "gpt-image-2:medium",
1123
+ "4k": "gpt-image-2:high"
1124
+ },
1125
+ falLedgerSuffix: {
1126
+ "1k": "gpt-image-2:1k",
1127
+ "2k": "gpt-image-2:2k",
1128
+ "4k": "gpt-image-2:4k"
1129
+ }
1130
+ };
1131
+ var GPT_IMAGE_2 = {
1132
+ ...GPT_IMAGE_2_5,
1133
+ id: "gpt-image-2",
1134
+ status: "retired",
1135
+ renderedBy: GPT_IMAGE_2_5.id,
1136
+ tokens: GPT_IMAGE_2_TOKENS,
1137
+ kie: {
1138
+ imageToImage: [
1139
+ "gpt-image-2-image-to-image"
1140
+ ],
1141
+ textToImage: [
1142
+ "gpt-image-2-text-to-image"
1143
+ ],
1144
+ shorthands: [
1145
+ "gpt-image-2"
1146
+ ]
1147
+ },
1148
+ ratiosBlockedAbove1k: [
1149
+ "4:5",
1150
+ "5:4"
1151
+ ]
1152
+ };
1153
+ var IMAGE_MODELS = [
1154
+ GPT_IMAGE_2_5,
1155
+ GPT_IMAGE_2
1054
1156
  ];
1055
- function gptImage2SupportsTier(aspectRatio, tier) {
1056
- if (tier === "gpt-image-2:1k" || tier === "gpt-image-2-5:1k") return true;
1057
- if (tier.startsWith("gpt-image-2-5:")) {
1058
- return !GPT_IMAGE_2_5_RATIOS_BLOCKED_ABOVE_1K.includes(aspectRatio);
1157
+ var ACTIVE_IMAGE_MODEL = GPT_IMAGE_2_5;
1158
+ var IMAGE_WRITE_TOKENS = GPT_IMAGE_2_5_TOKENS;
1159
+ var LEGACY_IMAGE_TOKENS = GPT_IMAGE_2_TOKENS;
1160
+ var PUBLIC_IMAGE_QUALITY_INPUTS = [
1161
+ IMAGE_WRITE_TOKENS["1k"],
1162
+ IMAGE_WRITE_TOKENS["2k"],
1163
+ LEGACY_IMAGE_TOKENS["1k"],
1164
+ LEGACY_IMAGE_TOKENS["2k"],
1165
+ "image:1k",
1166
+ "image:2k"
1167
+ ];
1168
+ function entryById(id) {
1169
+ const found = IMAGE_MODELS.find((entry) => entry.id === id);
1170
+ if (!found) throw new Error(`IMAGE_MODEL_UNKNOWN:${id}`);
1171
+ return found;
1172
+ }
1173
+ __name(entryById, "entryById");
1174
+ function resolveImageToken(raw) {
1175
+ if (typeof raw !== "string") return null;
1176
+ let normalized = raw.trim().toLowerCase();
1177
+ let viaFal = false;
1178
+ if (normalized.startsWith("fal:")) {
1179
+ viaFal = true;
1180
+ normalized = normalized.slice("fal:".length);
1181
+ }
1182
+ for (const resolution of IMAGE_RESOLUTIONS) {
1183
+ if (normalized === `image:${resolution}`) {
1184
+ return {
1185
+ resolution,
1186
+ model: ACTIVE_IMAGE_MODEL,
1187
+ declared: ACTIVE_IMAGE_MODEL,
1188
+ spelling: "intent",
1189
+ viaFal
1190
+ };
1191
+ }
1059
1192
  }
1060
- return !GPT_IMAGE_2_RATIOS_BLOCKED_ABOVE_1K.includes(aspectRatio);
1193
+ for (const entry of IMAGE_MODELS) {
1194
+ for (const resolution of IMAGE_RESOLUTIONS) {
1195
+ if (entry.tokens[resolution] === normalized) {
1196
+ return {
1197
+ resolution,
1198
+ model: entryById(entry.renderedBy),
1199
+ declared: entry,
1200
+ spelling: entry.status === "active" ? "current" : "retired",
1201
+ viaFal
1202
+ };
1203
+ }
1204
+ }
1205
+ }
1206
+ return null;
1207
+ }
1208
+ __name(resolveImageToken, "resolveImageToken");
1209
+ function imageResolutionOf(raw) {
1210
+ const token = resolveImageToken(raw);
1211
+ return token && !token.viaFal ? token.resolution : null;
1212
+ }
1213
+ __name(imageResolutionOf, "imageResolutionOf");
1214
+ function requireKieImageId(ids, index, label) {
1215
+ const id = ids[index];
1216
+ if (!id) throw new Error(`IMAGE_MODEL_NO_KIE_ID:${label}`);
1217
+ return id;
1218
+ }
1219
+ __name(requireKieImageId, "requireKieImageId");
1220
+ var KIE_FLARE_IMAGE_TO_IMAGE_MODEL = requireKieImageId(GPT_IMAGE_2_5.kie.imageToImage, 1, "flare-imageToImage");
1221
+ function canonicalImageModelInput(raw) {
1222
+ if (raw === void 0) return void 0;
1223
+ const token = resolveImageToken(raw);
1224
+ if (!token || token.viaFal || token.spelling !== "intent") return raw;
1225
+ return IMAGE_WRITE_TOKENS[token.resolution];
1226
+ }
1227
+ __name(canonicalImageModelInput, "canonicalImageModelInput");
1228
+ function imageTokenSupportsRatio(aspectRatio, rawToken) {
1229
+ const token = resolveImageToken(rawToken);
1230
+ if (!token) return true;
1231
+ if (token.resolution === "1k") return true;
1232
+ return !token.declared.ratiosBlockedAbove1k.includes(aspectRatio);
1233
+ }
1234
+ __name(imageTokenSupportsRatio, "imageTokenSupportsRatio");
1235
+
1236
+ // ../../packages/types/src/image-provider.ts
1237
+ var GPT_IMAGE_2_RATIOS_BLOCKED_ABOVE_1K = GPT_IMAGE_2.ratiosBlockedAbove1k;
1238
+ var GPT_IMAGE_2_5_RATIOS_BLOCKED_ABOVE_1K = GPT_IMAGE_2_5.ratiosBlockedAbove1k;
1239
+ function gptImage2SupportsTier(aspectRatio, tier) {
1240
+ return imageTokenSupportsRatio(aspectRatio, tier);
1061
1241
  }
1062
1242
  __name(gptImage2SupportsTier, "gptImage2SupportsTier");
1063
1243
  var IMAGE_TIER_UNSUPPORTED_FOR_RATIO_RULE = "image_tier_unsupported_for_ratio";
1064
1244
  var IMAGE_TIER_LIST = [
1065
- "gpt-image-2-5:1k",
1066
- "gpt-image-2-5:2k",
1067
- "gpt-image-2-5:4k",
1068
- "gpt-image-2:1k",
1069
- "gpt-image-2:2k",
1070
- "gpt-image-2:4k"
1245
+ ...Object.values(IMAGE_WRITE_TOKENS),
1246
+ ...Object.values(LEGACY_IMAGE_TOKENS)
1071
1247
  ];
1248
+ function isImageTier(value) {
1249
+ return IMAGE_TIER_LIST.includes(value);
1250
+ }
1251
+ __name(isImageTier, "isImageTier");
1072
1252
  function parseImageTier(imageModel) {
1073
- const normalized = imageModel.trim().toLowerCase();
1074
- return IMAGE_TIER_LIST.includes(normalized) ? normalized : null;
1253
+ const token = resolveImageToken(imageModel);
1254
+ if (!token || token.viaFal) return null;
1255
+ if (token.spelling === "intent") return IMAGE_WRITE_TOKENS[token.resolution];
1256
+ const declared = token.declared.tokens[token.resolution];
1257
+ return isImageTier(declared) ? declared : null;
1075
1258
  }
1076
1259
  __name(parseImageTier, "parseImageTier");
1077
1260
  function validateImageTierForRatio(aspectRatio, imageModel) {
@@ -1647,6 +1830,9 @@ var VideoCreativeJobStatus = z14.object({
1647
1830
  var VideoCreativeJobPayload = z14.object({
1648
1831
  jobId: z14.string().optional(),
1649
1832
  status: VideoCreativeJobStatusEnum.optional(),
1833
+ /** True when the submission replayed an existing task instead of billing
1834
+ * new work — callers surface the twin verbatim. Omitted on fresh creates. */
1835
+ replayed: z14.boolean().optional(),
1650
1836
  videoStructure: VideoStructure.optional(),
1651
1837
  videoQuality: VideoQuality.optional(),
1652
1838
  videoRouteId: VideoRouteId.optional(),
@@ -1740,7 +1926,8 @@ var ImageCreativeJobStatus = z14.object({
1740
1926
  });
1741
1927
  var ImageCreativeJobPayload = z14.object({
1742
1928
  jobId: z14.string().uuid(),
1743
- status: ImageCreativeJobStatusEnum
1929
+ status: ImageCreativeJobStatusEnum,
1930
+ replayed: z14.boolean().optional()
1744
1931
  });
1745
1932
  var ImageCreativeHistoryEntry = z14.object({
1746
1933
  id: z14.string().uuid(),
@@ -1767,7 +1954,8 @@ var LandingCreativeJobStatus = z14.object({
1767
1954
  });
1768
1955
  var LandingCreativeJobPayload = z14.object({
1769
1956
  jobId: z14.string().uuid(),
1770
- status: LandingCreativeJobStatusEnum
1957
+ status: LandingCreativeJobStatusEnum,
1958
+ replayed: z14.boolean().optional()
1771
1959
  });
1772
1960
  var LandingCreativeHistoryEntry = z14.object({
1773
1961
  id: z14.string().uuid(),
@@ -3808,7 +3996,7 @@ var IngestOptions = z22.object({
3808
3996
  durationSeconds: FreeVideoOptionsSchema.shape.durationSeconds.optional(),
3809
3997
  addSubtitles: FreeVideoOptionsSchema.shape.addSubtitles.optional(),
3810
3998
  subtitleText: z22.string().transform((value) => value.trim()).pipe(z22.string().max(FREE_VIDEO_SUBTITLE_MAX_CHARS)).optional(),
3811
- videoTier: VideoTier.default(DEFAULT_VIDEO_TIER),
3999
+ videoTier: WritableVideoTier.default(DEFAULT_VIDEO_TIER),
3812
4000
  narrativeStyle: NarrativeStyle.default("review"),
3813
4001
  videoPreset: VideoPreset.optional(),
3814
4002
  multiAngleVideos: z22.boolean().default(false),
@@ -3882,7 +4070,9 @@ var IngestOptions = z22.object({
3882
4070
  aspectRatio: z22.string().optional(),
3883
4071
  brandColors: z22.string().optional(),
3884
4072
  nVariants: z22.number().int().min(1).max(6).optional(),
3885
- imageModel: z22.string().optional(),
4073
+ // O7: `image:Nk` is accepted and stored as the spelling written today
4074
+ // (IMAGE_WRITE_TOKENS); see packages/types/src/image-models.ts.
4075
+ imageModel: z22.string().optional().transform((value) => canonicalImageModelInput(value)),
3886
4076
  styleMix: z22.string().optional(),
3887
4077
  aiDecidesAll: z22.boolean().optional(),
3888
4078
  /** Opt out of the competitor-spy analysis. When false the worker skips
@@ -4001,7 +4191,7 @@ var VideoOptionsOverride = z22.object({
4001
4191
  videoStructure: VideoStructure.optional(),
4002
4192
  videoQuality: VideoQuality.optional(),
4003
4193
  videoModel: VideoModel.optional(),
4004
- videoTier: VideoTier.optional(),
4194
+ videoTier: WritableVideoTier.optional(),
4005
4195
  ugcFormat: VideoFormat.optional(),
4006
4196
  videoStyle: VideoStyle.optional(),
4007
4197
  narrativeStyle: NarrativeStyle.optional(),
@@ -4084,6 +4274,11 @@ var IngestResumePayload = z22.object({
4084
4274
  // "keep what the task has". See angle-management.service.ts resumeAfterAngles.
4085
4275
  videoStyle: VideoStyle.optional(),
4086
4276
  videoModel: VideoModel.default("veo"),
4277
+ // READ schema on purpose, unlike IngestOptions/VideoOptionsOverride: resume
4278
+ // does not write the tier — `resumeAfterAngles` projects the route from the
4279
+ // task's OWN persisted tier and ignores this field. MCP and WhatsApp echo
4280
+ // `status.videoTier`, which is still `legacy` for historical rows; refusing
4281
+ // it made those tasks impossible to resume from both channels.
4087
4282
  videoTier: VideoTier.optional(),
4088
4283
  videoStructure: VideoStructure.optional(),
4089
4284
  videoQuality: VideoQuality.optional(),
@@ -4894,17 +5089,7 @@ var VideoGenerator = z26.object({
4894
5089
  format: VideoFormat.optional(),
4895
5090
  videoStyle: VideoStyle.optional(),
4896
5091
  narrativeStyle: NarrativeStyle.optional(),
4897
- videoMode: z26.enum([
4898
- "veo",
4899
- "wan",
4900
- "bytedance",
4901
- "seedance",
4902
- "grok",
4903
- "kling",
4904
- "avatar_talking",
4905
- "hybrid",
4906
- "sora"
4907
- ]).optional(),
5092
+ videoMode: PersistedVideoMode.optional(),
4908
5093
  videoResolution: VideoResolution.optional(),
4909
5094
  videoStructure: VideoStructure.optional(),
4910
5095
  videoQuality: VideoQuality.optional(),
@@ -5441,6 +5626,10 @@ var CreateShopifySubscriptionRequest = z28.object({
5441
5626
  var CreateShopifyBillingChargeRequest = z28.object({
5442
5627
  packId: z28.string().min(1)
5443
5628
  });
5629
+ var ShopifyBillingCallbackQuery = z28.object({
5630
+ charge_id: z28.string().regex(/^\d+$/),
5631
+ transactionId: z28.string().uuid()
5632
+ });
5444
5633
  var CancelShopifySubscriptionRequest = z28.object({
5445
5634
  subscriptionId: z28.string().min(1)
5446
5635
  });
@@ -7269,7 +7458,8 @@ var IMAGE_QA_GATES = [
7269
7458
  "limb",
7270
7459
  "device_panel",
7271
7460
  "critical_feature",
7272
- "cross_section"
7461
+ "cross_section",
7462
+ "usage_fidelity"
7273
7463
  ];
7274
7464
  var ImageQaGate = z36.enum(IMAGE_QA_GATES);
7275
7465
  var ImageQaGateOutcome = z36.enum([
@@ -8315,6 +8505,10 @@ var GEMINI_OMNI_FLAT_USER_PRICE_CENTS = 168;
8315
8505
  // ../../packages/types/src/pricing.ts
8316
8506
  var CREDITS_PER_USD_CENT = 10;
8317
8507
  var CREDITS_PER_DOLLAR = 100 * CREDITS_PER_USD_CENT;
8508
+ var AD_CREATIVE_MODELS = {
8509
+ STANDARD: IMAGE_WRITE_TOKENS["1k"],
8510
+ PREMIUM: IMAGE_WRITE_TOKENS["2k"]
8511
+ };
8318
8512
  var KALODATA_USD_CENTS_PER_PROVIDER_CREDIT = 9;
8319
8513
  var KALODATA_PROVIDER_CREDITS_PER_SOURCE_CEILING = 7.5;
8320
8514
  var DEFAULT_PRICING = {
@@ -9273,6 +9467,27 @@ var DEFAULT_PRICING = {
9273
9467
  ourCostCents: 1.88,
9274
9468
  userPriceCents: 3.76
9275
9469
  },
9470
+ // Usage decider (role `usage-decider`: Gemini 3.8 Flash effort:'high' since
9471
+ // 05f543eae, ≤6 images + seller text, temperature 0 json_object, once per
9472
+ // classic landing, always on). Was 3¢ measured on Claude
9473
+ // Sonnet 5 (spike 2026-09-24, 17 prod tasks). NOT re-measured on Flash — no
9474
+ // local BillingEntry row carries the Flash decider yet — so it is priced as
9475
+ // the product truth (`openrouter:product-truth`, 2.2¢ measured from
9476
+ // OpenRouter usage.cost): same model, same effort, same ≤6-photo input and a
9477
+ // JSON answer of similar size. Refine from usage.cost after the e2e. Fallback
9478
+ // only (OpenRouter's usage.cost is billed when present).
9479
+ "openrouter:usage-decision": {
9480
+ ourCostCents: 2.2,
9481
+ userPriceCents: 4.4
9482
+ },
9483
+ // Usage-fidelity QA judge (records misuse; regen gated separately behind
9484
+ // always on, bounded by USAGE_FIDELITY_MAX_REGENS). Sized off landing-image-qa beside it: same
9485
+ // shape of call (1-2 images, a verdict), so quoted at the same rate until
9486
+ // it has its own BillingEntry volume to re-derive from.
9487
+ "openrouter:usage-fidelity": {
9488
+ ourCostCents: 1.88,
9489
+ userPriceCents: 3.76
9490
+ },
9276
9491
  // Landing commercial-facts extraction (gemini-3.8-flash, ONE text-only call over
9277
9492
  // the seller's offer/context/features). Sized off the image-QA key beside it,
9278
9493
  // which measured 1.88¢ at ~11.1K prompt tokens: this prompt is capped at ~3K in
@@ -10011,6 +10226,24 @@ var DEFAULT_PRICING = {
10011
10226
  ourCostCents: 0.2,
10012
10227
  userPriceCents: 0.4
10013
10228
  },
10229
+ // Product truth (Gemini 3.8 Flash effort:'high', ≤6 photos + all seller text, once per
10230
+ // extract, always on). Plan C spike 2026-09-24: 6 calls, US$0.132 total ⇒
10231
+ // 2.2¢/call measured from OpenRouter usage.cost. Replaces usage spec + functional demo +
10232
+ // product-use sheet (+ landing visual-context-fusion) on the same task. Fallback only.
10233
+ "openrouter:product-truth": {
10234
+ ourCostCents: 2.2,
10235
+ userPriceCents: 4.4
10236
+ },
10237
+ // Landing creative director, «categorías» arm (Gemini 3.8 Flash effort:'high', ≤6 photos +
10238
+ // all seller text + the section plan, once per classic landing behind
10239
+ // LANDING_DIRECTOR_ENABLED). ESTIMATE, not measured: same call shape as the product truth
10240
+ // (2.2¢) with a ~2-3x longer JSON answer (one content spec per section) ⇒ ~4¢. Replaces
10241
+ // the visual brief (4¢) + visual-context fusion (0.4¢) + usage decision (2.2¢) on that
10242
+ // landing. Refine from OpenRouter usage.cost after the e2e. Fallback only.
10243
+ "openrouter:landing-director": {
10244
+ ourCostCents: 4,
10245
+ userPriceCents: 8
10246
+ },
10014
10247
  // Unit = one reference-grouping pass over the user's product images (0.1241¢).
10015
10248
  "openrouter:product-reference-grouping": {
10016
10249
  ourCostCents: 0.15,
@@ -10624,6 +10857,7 @@ var MixSceneResult = z48.object({
10624
10857
  index: z48.number().int().positive(),
10625
10858
  status: z48.enum([
10626
10859
  "queued",
10860
+ "submitting",
10627
10861
  "submitted",
10628
10862
  "done",
10629
10863
  "failed"
@@ -13222,7 +13456,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13222
13456
  ], [
13223
13457
  "product_costing"
13224
13458
  ]),
13225
- defineCapability("create_creative_variant", "6a777fbe7ad17966d16502e8872e15e4db2dcfacdfd2992d24853340d4f22a00", "credit_consuming", "creative", [
13459
+ defineCapability("create_creative_variant", "3e02399a7785df328f1c4d63cfbb6d7481abaac9f91d4a5cf51d410e657d63aa", "credit_consuming", "creative", [
13226
13460
  "credit_consuming",
13227
13461
  "state_change"
13228
13462
  ], [
@@ -13472,7 +13706,9 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13472
13706
  // MCP 7.0.0 (Task 11, .agent/plans/2026-09-15-model-agnostic-video-routes.md):
13473
13707
  // `freeProModel` retired in favor of `modelId` (registry ids). Recomputed
13474
13708
  // from `pnpm --filter @productmaker/mcp test` printing the real digest.
13475
- "ead1f563a1915452157628d023ba203ecfa8f502a48259a5b5b15be4c84bf768",
13709
+ // Re-baselined 2026-09-26: `idempotencyKey` field description corrected —
13710
+ // paid tools ignore caller-provided keys and derive their own semantic key.
13711
+ "35616a32a42a0bc062218fdfbb4cc7f4ecfa814571fb86b4f11ef9e4184855e1",
13476
13712
  "credit_consuming",
13477
13713
  "creative",
13478
13714
  [
@@ -13528,7 +13764,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13528
13764
  "video"
13529
13765
  ]
13530
13766
  ),
13531
- defineCapability("generate_image_creatives", "eb2c3f6a0d8974ccf33c42f2b4cc9e09918dabf29182acce1418ea2842a01ec3", "credit_consuming", "creative", [
13767
+ defineCapability("generate_image_creatives", "2f1698b623d98645b7c618a17988f3aac30f403a9c3f3dc26eb5bdbd9c6ef23e", "credit_consuming", "creative", [
13532
13768
  "credit_consuming",
13533
13769
  "state_change"
13534
13770
  ], [
@@ -15179,79 +15415,696 @@ var WhatsAppPresentationManifestSchema = z67.object({
15179
15415
  }
15180
15416
  });
15181
15417
 
15182
- // ../../packages/types/src/flight-recorder.ts
15418
+ // ../../packages/types/src/usage-decision.ts
15183
15419
  import { z as z68 } from "zod";
15184
- var FlightRecorderGenerationTypeSchema = z68.enum([
15420
+
15421
+ // ../../packages/types/src/text-clamp.ts
15422
+ function clampAtWordBoundary(value, max) {
15423
+ if (typeof value !== "string") return value;
15424
+ if (value.length <= max) return value;
15425
+ const cut = value.slice(0, max);
15426
+ const lastSpace = cut.lastIndexOf(" ");
15427
+ const atWordBoundary = lastSpace > 0 ? cut.slice(0, lastSpace) : cut;
15428
+ return atWordBoundary.replace(/[,;:\s]+$/, "");
15429
+ }
15430
+ __name(clampAtWordBoundary, "clampAtWordBoundary");
15431
+ function clampStringArray(max) {
15432
+ return (value) => {
15433
+ if (!Array.isArray(value)) return value;
15434
+ const nonEmpty = value.filter((item) => typeof item === "string" && item.trim().length > 0);
15435
+ return nonEmpty.slice(0, max);
15436
+ };
15437
+ }
15438
+ __name(clampStringArray, "clampStringArray");
15439
+
15440
+ // ../../packages/types/src/usage-decision.ts
15441
+ var USAGE_SCENE_RULE_MAX_CHARS = 240;
15442
+ var USAGE_DECISION_MAX_REQUIRED_PARTS = 6;
15443
+ var USAGE_DECISION_MAX_ALTERNATE_USES = 4;
15444
+ var USAGE_DECISION_MAX_MISUSE = 5;
15445
+ function clampRule(value) {
15446
+ return clampAtWordBoundary(value, USAGE_SCENE_RULE_MAX_CHARS);
15447
+ }
15448
+ __name(clampRule, "clampRule");
15449
+ var UsageDecisionSchema = z68.object({
15450
+ productIdentity: z68.string().min(1),
15451
+ endUser: z68.object({
15452
+ who: z68.string(),
15453
+ appliedTo: z68.string()
15454
+ }),
15455
+ promise: z68.string().min(1),
15456
+ primaryUse: z68.object({
15457
+ scene: z68.string().min(1),
15458
+ postureAndGrip: z68.string(),
15459
+ contactPoint: z68.string(),
15460
+ requiredParts: z68.preprocess(clampStringArray(USAGE_DECISION_MAX_REQUIRED_PARTS), z68.array(z68.string()).max(USAGE_DECISION_MAX_REQUIRED_PARTS))
15461
+ }),
15462
+ validAlternateUses: z68.preprocess(clampStringArray(USAGE_DECISION_MAX_ALTERNATE_USES), z68.array(z68.string()).max(USAGE_DECISION_MAX_ALTERNATE_USES)),
15463
+ misuse: z68.preprocess(clampStringArray(USAGE_DECISION_MAX_MISUSE), z68.array(z68.string().min(1)).max(USAGE_DECISION_MAX_MISUSE)),
15464
+ useSceneRule: z68.preprocess(clampRule, z68.string().min(1).max(USAGE_SCENE_RULE_MAX_CHARS)),
15465
+ /**
15466
+ * True when the promise can ONLY be shown with the primary use (the pain is bending over and
15467
+ * the product has an extension pole: without the pole there is no promise); false when the
15468
+ * valid alternates sell it equally. Drives whether an alternate use in a use scene is a
15469
+ * failure (`requirePrimary` in the usage judge) and whether the prompts' primary use is
15470
+ * mandatory. Optional and fail-safe: missing (decisions stored before the field) or not a
15471
+ * boolean ⇒ `undefined`, read as false by every caller — never a rejected decision.
15472
+ */
15473
+ primaryRequired: z68.boolean().optional().catch(void 0),
15474
+ confidence: z68.number().min(0).max(1),
15475
+ modelId: z68.string(),
15476
+ decidedAt: z68.string()
15477
+ });
15478
+
15479
+ // ../../packages/types/src/product-truth.ts
15480
+ import { z as z70 } from "zod";
15481
+
15482
+ // ../../packages/types/src/lenient-zod.ts
15483
+ import { z as z69 } from "zod";
15484
+ function trimAndClamp(value, max) {
15485
+ if (typeof value !== "string") return value;
15486
+ return clampAtWordBoundary(value.replace(/\s+/g, " ").trim(), max);
15487
+ }
15488
+ __name(trimAndClamp, "trimAndClamp");
15489
+ function lenientText(max) {
15490
+ return z69.preprocess((v) => trimAndClamp(v, max), z69.string().min(1).max(max));
15491
+ }
15492
+ __name(lenientText, "lenientText");
15493
+ function lenientOptionalText(max) {
15494
+ return z69.preprocess((v) => {
15495
+ if (typeof v !== "string") return void 0;
15496
+ const clamped = trimAndClamp(v, max);
15497
+ return typeof clamped === "string" && clamped.length > 0 ? clamped : void 0;
15498
+ }, z69.string().max(max).optional());
15499
+ }
15500
+ __name(lenientOptionalText, "lenientOptionalText");
15501
+ function lenientTextList(maxItems, maxChars) {
15502
+ return z69.preprocess((v) => {
15503
+ if (!Array.isArray(v)) return [];
15504
+ return v.map((item) => trimAndClamp(item, maxChars)).filter((item) => typeof item === "string" && item.length > 0).slice(0, maxItems);
15505
+ }, z69.array(z69.string().min(1).max(maxChars)).max(maxItems));
15506
+ }
15507
+ __name(lenientTextList, "lenientTextList");
15508
+ function lenientList(item, maxItems) {
15509
+ return z69.preprocess((v) => {
15510
+ if (!Array.isArray(v)) return [];
15511
+ return v.filter((entry) => item.safeParse(entry).success).slice(0, maxItems);
15512
+ }, z69.array(item).max(maxItems));
15513
+ }
15514
+ __name(lenientList, "lenientList");
15515
+
15516
+ // ../../packages/types/src/product-truth.ts
15517
+ var PRODUCT_TRUTH_SCHEMA_VERSION = 1;
15518
+ var PRODUCT_TRUTH_CAPS = {
15519
+ whatItIs: 160,
15520
+ deliveryForm: 120,
15521
+ distinctiveMarks: 6,
15522
+ distinctiveMarkChars: 160,
15523
+ parts: 12,
15524
+ partNameChars: 80,
15525
+ criticalFeatures: 6,
15526
+ connections: 6,
15527
+ connectionChars: 80,
15528
+ states: 4,
15529
+ stateKeyChars: 60,
15530
+ stateLabelChars: 80,
15531
+ partsPresent: 10,
15532
+ orientationInUseChars: 200,
15533
+ appliedToChars: 120,
15534
+ environmentChars: 120,
15535
+ summaryChars: 200,
15536
+ sceneChars: 300,
15537
+ postureAndGripChars: 160,
15538
+ contactPointChars: 120,
15539
+ steps: 8,
15540
+ stepNameChars: 120,
15541
+ stepInstructionChars: 300,
15542
+ visibleEffectChars: 200,
15543
+ beforeStateChars: 200,
15544
+ requiredProps: 8,
15545
+ requiredPropChars: 120,
15546
+ alternates: 3,
15547
+ misuse: 5,
15548
+ misuseChars: 200,
15549
+ mustShowFeatures: 5,
15550
+ mustShowFeatureChars: 100,
15551
+ safety: 5,
15552
+ safetyChars: 200,
15553
+ forbiddenClaims: 8,
15554
+ unsupportedClaims: 5,
15555
+ claimChars: 200,
15556
+ visualSummaryEnChars: 400
15557
+ };
15558
+ var PRODUCT_TRUTH_END_USER_AGE_BANDS = [
15559
+ "baby",
15560
+ "child",
15561
+ "teen",
15562
+ "adult",
15563
+ "any"
15564
+ ];
15565
+ var PRODUCT_TRUTH_END_USER_GENDERS = [
15566
+ "female",
15567
+ "male",
15568
+ "any"
15569
+ ];
15570
+ var PRODUCT_TRUTH_UNKNOWN_REASONS = [
15571
+ "model_unsure",
15572
+ "no_signal",
15573
+ "invalid_response",
15574
+ "provider_error"
15575
+ ];
15576
+ var text = lenientText;
15577
+ var optionalText = lenientOptionalText;
15578
+ var textList = lenientTextList;
15579
+ function toProductTruthStateKey(value) {
15580
+ if (typeof value !== "string") return void 0;
15581
+ const slug = value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, PRODUCT_TRUTH_CAPS.stateKeyChars);
15582
+ return slug.length > 0 ? slug : void 0;
15583
+ }
15584
+ __name(toProductTruthStateKey, "toProductTruthStateKey");
15585
+ var stateKey = z70.preprocess(toProductTruthStateKey, z70.string().min(1).max(PRODUCT_TRUTH_CAPS.stateKeyChars));
15586
+ var optionalStateKey = z70.preprocess(toProductTruthStateKey, z70.string().min(1).max(PRODUCT_TRUTH_CAPS.stateKeyChars).optional());
15587
+ var AGE_BAND_ALIASES = {
15588
+ baby: "baby",
15589
+ babies: "baby",
15590
+ infant: "baby",
15591
+ infants: "baby",
15592
+ toddler: "child",
15593
+ child: "child",
15594
+ children: "child",
15595
+ kid: "child",
15596
+ kids: "child",
15597
+ teen: "teen",
15598
+ teens: "teen",
15599
+ teenager: "teen",
15600
+ teenagers: "teen",
15601
+ adult: "adult",
15602
+ adults: "adult",
15603
+ any: "any",
15604
+ all: "any",
15605
+ "all ages": "any"
15606
+ };
15607
+ var GENDER_ALIASES = {
15608
+ female: "female",
15609
+ woman: "female",
15610
+ women: "female",
15611
+ girl: "female",
15612
+ male: "male",
15613
+ man: "male",
15614
+ men: "male",
15615
+ boy: "male",
15616
+ any: "any",
15617
+ unisex: "any",
15618
+ both: "any",
15619
+ all: "any"
15620
+ };
15621
+ function matchAlias(raw, aliases) {
15622
+ if (typeof raw !== "string") return void 0;
15623
+ const text2 = raw.trim().toLowerCase();
15624
+ const exact = aliases[text2];
15625
+ if (exact) return exact;
15626
+ const words = new Set(text2.split(/[^a-z]+/).filter(Boolean));
15627
+ for (const [alias, value] of Object.entries(aliases)) {
15628
+ if (words.has(alias)) return value;
15629
+ }
15630
+ return void 0;
15631
+ }
15632
+ __name(matchAlias, "matchAlias");
15633
+ function normalizeEndUser(value) {
15634
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
15635
+ const v = value;
15636
+ const age = matchAlias(v.ageBand, AGE_BAND_ALIASES);
15637
+ const gender = matchAlias(v.gender, GENDER_ALIASES);
15638
+ if (!age || !gender || typeof v.usedByBuyer !== "boolean") return void 0;
15639
+ return {
15640
+ ageBand: age,
15641
+ gender,
15642
+ usedByBuyer: v.usedByBuyer
15643
+ };
15644
+ }
15645
+ __name(normalizeEndUser, "normalizeEndUser");
15646
+ var ProductTruthEndUserSchema = z70.object({
15647
+ ageBand: z70.enum(PRODUCT_TRUTH_END_USER_AGE_BANDS),
15648
+ gender: z70.enum(PRODUCT_TRUTH_END_USER_GENDERS),
15649
+ usedByBuyer: z70.boolean()
15650
+ });
15651
+ var MATERIAL_HINTS = ProductMorphology.shape.materialHints.unwrap().element.options;
15652
+ var DEMO_TYPES = FunctionalDemonstration.shape.demoType;
15653
+ var PartSchema = z70.object({
15654
+ name: text(PRODUCT_TRUTH_CAPS.partNameChars),
15655
+ kind: z70.enum([
15656
+ "rigid",
15657
+ "flexible",
15658
+ "joint"
15659
+ ]).catch("rigid"),
15660
+ count: z70.number().int().min(1).max(20).catch(1)
15661
+ });
15662
+ var CriticalFeatureSchema = z70.object({
15663
+ name: text(60),
15664
+ importance: text(120),
15665
+ antiPattern: optionalText(80),
15666
+ expectedCount: z70.number().int().min(1).max(20).optional().catch(void 0)
15667
+ });
15668
+ var ConnectionSchema = z70.object({
15669
+ part: text(PRODUCT_TRUTH_CAPS.connectionChars),
15670
+ attachesTo: text(PRODUCT_TRUTH_CAPS.connectionChars),
15671
+ where: optionalText(PRODUCT_TRUTH_CAPS.connectionChars)
15672
+ });
15673
+ var StateSchema = z70.object({
15674
+ key: stateKey,
15675
+ label: optionalText(PRODUCT_TRUTH_CAPS.stateLabelChars),
15676
+ partsPresent: textList(PRODUCT_TRUTH_CAPS.partsPresent, PRODUCT_TRUTH_CAPS.partNameChars),
15677
+ orientationInUse: text(PRODUCT_TRUTH_CAPS.orientationInUseChars),
15678
+ naturalOrientation: ProductNaturalOrientation.optional().catch(void 0)
15679
+ });
15680
+ var StepSchema = z70.object({
15681
+ name: text(PRODUCT_TRUTH_CAPS.stepNameChars),
15682
+ instruction: text(PRODUCT_TRUTH_CAPS.stepInstructionChars),
15683
+ kind: z70.enum([
15684
+ "setup",
15685
+ "use",
15686
+ "finish"
15687
+ ]).catch("use")
15688
+ });
15689
+ var AlternateSchema = z70.object({
15690
+ summary: text(PRODUCT_TRUTH_CAPS.summaryChars),
15691
+ stateKey: optionalStateKey,
15692
+ advertisedBySeller: z70.boolean().catch(false)
15693
+ });
15694
+ var DemoSchema = z70.object({
15695
+ demoType: DEMO_TYPES.nullable().catch(null),
15696
+ mustShowFeatures: textList(PRODUCT_TRUTH_CAPS.mustShowFeatures, PRODUCT_TRUTH_CAPS.mustShowFeatureChars),
15697
+ importance: z70.enum([
15698
+ "critical",
15699
+ "important",
15700
+ "nice_to_have"
15701
+ ]).catch("important")
15702
+ });
15703
+ var objectOrEmpty = /* @__PURE__ */ __name((v) => v && typeof v === "object" && !Array.isArray(v) ? v : {}, "objectOrEmpty");
15704
+ var ProductTruthBody = z70.object({
15705
+ identity: z70.object({
15706
+ whatItIs: text(PRODUCT_TRUTH_CAPS.whatItIs),
15707
+ deliveryForm: optionalText(PRODUCT_TRUTH_CAPS.deliveryForm),
15708
+ distinctiveMarks: textList(PRODUCT_TRUTH_CAPS.distinctiveMarks, PRODUCT_TRUTH_CAPS.distinctiveMarkChars)
15709
+ }),
15710
+ morphology: z70.preprocess(objectOrEmpty, z70.object({
15711
+ shape: ProductGeometryShape.optional().catch(void 0),
15712
+ crossSection: ProductCrossSection.optional().catch(void 0),
15713
+ cornerProfile: ProductCornerProfile.optional().catch(void 0),
15714
+ silhouetteWidthToHeightRatio: z70.number().min(0.05).max(20).optional().catch(void 0),
15715
+ parts: lenientList(PartSchema, PRODUCT_TRUTH_CAPS.parts),
15716
+ criticalFeatures: lenientList(CriticalFeatureSchema, PRODUCT_TRUTH_CAPS.criticalFeatures),
15717
+ shapeLockLevel: z70.enum([
15718
+ "strict",
15719
+ "moderate",
15720
+ "relaxed"
15721
+ ]).optional().catch(void 0),
15722
+ materialHints: z70.preprocess((v) => Array.isArray(v) ? [
15723
+ ...new Set(v.filter((m) => MATERIAL_HINTS.includes(m)))
15724
+ ].slice(0, 3) : [], z70.array(z70.enum(MATERIAL_HINTS)).max(3))
15725
+ })),
15726
+ assembly: z70.object({
15727
+ connections: lenientList(ConnectionSchema, PRODUCT_TRUTH_CAPS.connections),
15728
+ states: z70.preprocess((v) => {
15729
+ if (!Array.isArray(v)) return v;
15730
+ const seen = /* @__PURE__ */ new Set();
15731
+ return v.filter((entry) => {
15732
+ const parsed = StateSchema.safeParse(entry);
15733
+ if (!parsed.success || seen.has(parsed.data.key)) return false;
15734
+ seen.add(parsed.data.key);
15735
+ return true;
15736
+ }).slice(0, PRODUCT_TRUTH_CAPS.states);
15737
+ }, z70.array(StateSchema).min(1).max(PRODUCT_TRUTH_CAPS.states)),
15738
+ defaultStateKey: optionalStateKey
15739
+ }),
15740
+ use: z70.object({
15741
+ endUser: z70.preprocess(normalizeEndUser, ProductTruthEndUserSchema.optional()).catch(void 0),
15742
+ appliedTo: optionalText(PRODUCT_TRUTH_CAPS.appliedToChars),
15743
+ environment: optionalText(PRODUCT_TRUTH_CAPS.environmentChars),
15744
+ primary: z70.object({
15745
+ summary: text(PRODUCT_TRUTH_CAPS.summaryChars),
15746
+ scene: text(PRODUCT_TRUTH_CAPS.sceneChars),
15747
+ postureAndGrip: optionalText(PRODUCT_TRUTH_CAPS.postureAndGripChars),
15748
+ contactPoint: optionalText(PRODUCT_TRUTH_CAPS.contactPointChars),
15749
+ stateKey: optionalStateKey,
15750
+ 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)),
15751
+ visibleEffect: optionalText(PRODUCT_TRUTH_CAPS.visibleEffectChars),
15752
+ beforeState: optionalText(PRODUCT_TRUTH_CAPS.beforeStateChars),
15753
+ requiredProps: textList(PRODUCT_TRUTH_CAPS.requiredProps, PRODUCT_TRUTH_CAPS.requiredPropChars)
15754
+ }),
15755
+ alternates: lenientList(AlternateSchema, PRODUCT_TRUTH_CAPS.alternates),
15756
+ misuse: textList(PRODUCT_TRUTH_CAPS.misuse, PRODUCT_TRUTH_CAPS.misuseChars),
15757
+ demo: z70.preprocess(objectOrEmpty, DemoSchema),
15758
+ safety: textList(PRODUCT_TRUTH_CAPS.safety, PRODUCT_TRUTH_CAPS.safetyChars)
15759
+ }),
15760
+ claims: z70.preprocess(objectOrEmpty, z70.object({
15761
+ forbidden: textList(PRODUCT_TRUTH_CAPS.forbiddenClaims, PRODUCT_TRUTH_CAPS.claimChars),
15762
+ unsupported: textList(PRODUCT_TRUTH_CAPS.unsupportedClaims, PRODUCT_TRUTH_CAPS.claimChars)
15763
+ })),
15764
+ visualSummaryEn: optionalText(PRODUCT_TRUTH_CAPS.visualSummaryEnChars),
15765
+ evidence: z70.preprocess(objectOrEmpty, z70.object({
15766
+ photoIndexForState: z70.preprocess((v) => {
15767
+ const out = {};
15768
+ if (!v || typeof v !== "object" || Array.isArray(v)) return out;
15769
+ for (const [rawKey, rawIndexes] of Object.entries(v)) {
15770
+ const key = toProductTruthStateKey(rawKey);
15771
+ if (!key || !Array.isArray(rawIndexes)) continue;
15772
+ out[key] = rawIndexes.filter((i) => typeof i === "number" && Number.isInteger(i) && i >= 0 && i < 20);
15773
+ }
15774
+ return out;
15775
+ }, z70.record(z70.array(z70.number().int().min(0).max(19))))
15776
+ })),
15777
+ confidence: z70.preprocess((v) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : 0, z70.number().min(0).max(1))
15778
+ });
15779
+ function resolveStateRefs(body) {
15780
+ const keys = new Set(body.assembly.states.map((s) => s.key));
15781
+ const firstKey = body.assembly.states[0]?.key ?? "";
15782
+ const defaultStateKey = body.assembly.defaultStateKey && keys.has(body.assembly.defaultStateKey) ? body.assembly.defaultStateKey : firstKey;
15783
+ const primaryKey = body.use.primary.stateKey && keys.has(body.use.primary.stateKey) ? body.use.primary.stateKey : defaultStateKey;
15784
+ const photoIndexForState = Object.fromEntries(Object.entries(body.evidence.photoIndexForState).filter(([key]) => keys.has(key)));
15785
+ return {
15786
+ ...body,
15787
+ assembly: {
15788
+ ...body.assembly,
15789
+ defaultStateKey
15790
+ },
15791
+ use: {
15792
+ ...body.use,
15793
+ primary: {
15794
+ ...body.use.primary,
15795
+ stateKey: primaryKey
15796
+ },
15797
+ alternates: body.use.alternates.map((alt) => ({
15798
+ ...alt,
15799
+ stateKey: alt.stateKey && keys.has(alt.stateKey) ? alt.stateKey : defaultStateKey
15800
+ }))
15801
+ },
15802
+ evidence: {
15803
+ ...body.evidence,
15804
+ photoIndexForState
15805
+ }
15806
+ };
15807
+ }
15808
+ __name(resolveStateRefs, "resolveStateRefs");
15809
+ var ProductTruthModelResponseSchema = ProductTruthBody.transform(resolveStateRefs);
15810
+ var provenance = {
15811
+ schemaVersion: z70.literal(PRODUCT_TRUTH_SCHEMA_VERSION),
15812
+ modelId: z70.string().min(1),
15813
+ derivedAt: z70.string().min(1),
15814
+ /** Hash of (image keys + seller text + features): a reader that sees a different current
15815
+ * fingerprint treats the truth as absent. See `computeProductTruthFingerprint` in packages/ai. */
15816
+ sourceFingerprint: z70.string().min(1),
15817
+ /** The seller upload KEYS (deduped, sorted) the fingerprint was computed over. Stored so a
15818
+ * reader can recompute the CURRENT fingerprint from today's seller text + features without
15819
+ * knowing the extract job's inputs (plan Global Constraint "Invalidez"). */
15820
+ sourceImageKeys: z70.array(z70.string().min(1)).max(20).optional()
15821
+ };
15822
+ var ProductTruthKnownSchema = ProductTruthBody.extend({
15823
+ ...provenance,
15824
+ known: z70.literal(true)
15825
+ }).transform(resolveStateRefs);
15826
+ var ProductTruthUnknownSchema = z70.object({
15827
+ schemaVersion: z70.literal(PRODUCT_TRUTH_SCHEMA_VERSION),
15828
+ known: z70.literal(false),
15829
+ unknownReason: z70.enum(PRODUCT_TRUTH_UNKNOWN_REASONS),
15830
+ modelId: z70.string().optional(),
15831
+ derivedAt: z70.string().min(1),
15832
+ sourceFingerprint: z70.string().min(1),
15833
+ sourceImageKeys: z70.array(z70.string().min(1)).max(20).optional()
15834
+ });
15835
+ var ProductTruthSchema = z70.union([
15836
+ ProductTruthKnownSchema,
15837
+ ProductTruthUnknownSchema
15838
+ ]);
15839
+
15840
+ // ../../packages/types/src/landing-director.ts
15841
+ import { z as z71 } from "zod";
15842
+ var LANDING_DIRECTOR_SCHEMA_VERSION = 1;
15843
+ var LANDING_DIRECTOR_ARM = "categories";
15844
+ var LANDING_DIRECTOR_CAPS = {
15845
+ productIdentity: 200,
15846
+ parts: 8,
15847
+ partName: 40,
15848
+ promise: 200,
15849
+ endUserWho: 80,
15850
+ endUserAppliedTo: 80,
15851
+ forbiddenClaims: 10,
15852
+ claimChars: 80,
15853
+ misuse: 5,
15854
+ misuseChars: 120,
15855
+ assembledStates: 3,
15856
+ statePartsMax: 6,
15857
+ orientation: 160,
15858
+ handling: 140,
15859
+ primaryScene: 200,
15860
+ postureAndGrip: 140,
15861
+ contactPoint: 100,
15862
+ alternates: 3,
15863
+ alternateChars: 120,
15864
+ ambiguities: 4,
15865
+ ambiguityChars: 160,
15866
+ sectionKeyChars: 40,
15867
+ personWho: 80,
15868
+ personPosture: 120,
15869
+ personGrip: 120,
15870
+ scene: 450,
15871
+ panels: 4,
15872
+ panelScene: 200,
15873
+ panelCaption: 60,
15874
+ /** Rendered text: an item over its cap is DROPPED, never cut. The headline cap is 80, not
15875
+ * the design's 70: a dropped headline costs the whole director headline, and 7-10 Spanish
15876
+ * words routinely run 70-80 chars. */
15877
+ headline: 80,
15878
+ subheadline: 90,
15879
+ labels: 3,
15880
+ labelChars: 40,
15881
+ captions: 3,
15882
+ captionChars: 60,
15883
+ props: 4,
15884
+ propChars: 40,
15885
+ mustNotShow: 5,
15886
+ mustNotShowChars: 80
15887
+ };
15888
+ var STATE_KEY_PATTERN = /^[a-z][a-z_]{1,23}$/;
15889
+ function toLandingDirectorStateKey(value) {
15890
+ if (typeof value !== "string") return void 0;
15891
+ const slug = value.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 24).replace(/_+$/g, "");
15892
+ return STATE_KEY_PATTERN.test(slug) ? slug : void 0;
15893
+ }
15894
+ __name(toLandingDirectorStateKey, "toLandingDirectorStateKey");
15895
+ var stateKey2 = z71.preprocess(toLandingDirectorStateKey, z71.string().regex(STATE_KEY_PATTERN));
15896
+ function renderedOptionalText(max) {
15897
+ return z71.preprocess((v) => {
15898
+ if (typeof v !== "string") return void 0;
15899
+ const trimmed = v.replace(/\s+/g, " ").trim();
15900
+ return trimmed.length > 0 && trimmed.length <= max ? trimmed : void 0;
15901
+ }, z71.string().min(1).max(max).optional());
15902
+ }
15903
+ __name(renderedOptionalText, "renderedOptionalText");
15904
+ function renderedTextList(maxItems, maxChars) {
15905
+ return z71.preprocess((v) => {
15906
+ if (!Array.isArray(v)) return [];
15907
+ return v.map((item) => typeof item === "string" ? item.replace(/\s+/g, " ").trim() : "").filter((item) => item.length > 0 && item.length <= maxChars).slice(0, maxItems);
15908
+ }, z71.array(z71.string().min(1).max(maxChars)).max(maxItems));
15909
+ }
15910
+ __name(renderedTextList, "renderedTextList");
15911
+ var C = LANDING_DIRECTOR_CAPS;
15912
+ var PartSchema2 = z71.object({
15913
+ name: lenientText(C.partName),
15914
+ critical: z71.boolean().catch(false)
15915
+ });
15916
+ var AssembledStateSchema = z71.object({
15917
+ key: stateKey2,
15918
+ parts: lenientTextList(C.statePartsMax, C.partName),
15919
+ orientation: lenientText(C.orientation),
15920
+ handling: lenientOptionalText(C.handling)
15921
+ });
15922
+ var END_USER_GENDERS = [
15923
+ "female",
15924
+ "male",
15925
+ "any"
15926
+ ];
15927
+ var END_USER_AGE_BANDS = [
15928
+ "child",
15929
+ "teen",
15930
+ "adult",
15931
+ "senior",
15932
+ "any"
15933
+ ];
15934
+ var EndUserSchema = z71.object({
15935
+ who: lenientText(C.endUserWho),
15936
+ appliedTo: lenientOptionalText(C.endUserAppliedTo),
15937
+ gender: z71.enum(END_USER_GENDERS).catch("any"),
15938
+ ageBand: z71.enum(END_USER_AGE_BANDS).catch("any")
15939
+ });
15940
+ var LandingDirectorGlobalSchema = z71.object({
15941
+ productIdentity: lenientText(C.productIdentity),
15942
+ parts: lenientList(PartSchema2, C.parts),
15943
+ promise: lenientText(C.promise),
15944
+ endUser: EndUserSchema,
15945
+ forbiddenClaims: lenientTextList(C.forbiddenClaims, C.claimChars),
15946
+ misuse: lenientTextList(C.misuse, C.misuseChars),
15947
+ assembledStates: z71.preprocess((v) => {
15948
+ if (!Array.isArray(v)) return v;
15949
+ const seen = /* @__PURE__ */ new Set();
15950
+ return v.filter((entry) => {
15951
+ const parsed = AssembledStateSchema.safeParse(entry);
15952
+ if (!parsed.success || seen.has(parsed.data.key)) return false;
15953
+ seen.add(parsed.data.key);
15954
+ return true;
15955
+ }).slice(0, C.assembledStates);
15956
+ }, z71.array(AssembledStateSchema).min(1).max(C.assembledStates)),
15957
+ primaryUse: z71.object({
15958
+ assembledState: stateKey2,
15959
+ scene: lenientText(C.primaryScene),
15960
+ postureAndGrip: lenientText(C.postureAndGrip),
15961
+ contactPoint: lenientOptionalText(C.contactPoint)
15962
+ }),
15963
+ primaryRequired: z71.boolean().catch(false),
15964
+ validAlternateUses: lenientTextList(C.alternates, C.alternateChars),
15965
+ ambiguities: lenientTextList(C.ambiguities, C.ambiguityChars)
15966
+ });
15967
+ var LANDING_DIRECTOR_BODY_PARTS = [
15968
+ "hands",
15969
+ "face",
15970
+ "torso",
15971
+ "legs",
15972
+ "feet",
15973
+ "full_body"
15974
+ ];
15975
+ var PersonSchema = z71.object({
15976
+ who: lenientText(C.personWho),
15977
+ posture: lenientText(C.personPosture),
15978
+ grip: lenientOptionalText(C.personGrip),
15979
+ bodyPartsVisible: z71.preprocess((v) => Array.isArray(v) ? [
15980
+ ...new Set(v.filter((part) => LANDING_DIRECTOR_BODY_PARTS.includes(part)))
15981
+ ] : [], z71.array(z71.enum(LANDING_DIRECTOR_BODY_PARTS)))
15982
+ });
15983
+ var LandingDirectorTextSchema = z71.object({
15984
+ headline: renderedOptionalText(C.headline),
15985
+ subheadline: renderedOptionalText(C.subheadline),
15986
+ labels: renderedTextList(C.labels, C.labelChars),
15987
+ captions: renderedTextList(C.captions, C.captionChars)
15988
+ });
15989
+ var PanelSchema = z71.object({
15990
+ scene: lenientText(C.panelScene),
15991
+ caption: renderedOptionalText(C.panelCaption)
15992
+ });
15993
+ var PRODUCT_IN_FRAME_VALUES = [
15994
+ "none",
15995
+ "packshot",
15996
+ "in_use"
15997
+ ];
15998
+ var LandingDirectorSectionSchema = z71.object({
15999
+ sectionKey: z71.string().trim().min(1).max(C.sectionKeyChars),
16000
+ /** Present only in the «director» arm. Kept as a flag so the «categorías» validation can
16001
+ * invalidate the section (design §2.3) instead of silently dropping the model's choice. */
16002
+ hasComposition: z71.boolean().default(false),
16003
+ productInFrame: z71.enum(PRODUCT_IN_FRAME_VALUES),
16004
+ assembledState: z71.preprocess((v) => v === null || v === void 0 ? null : toLandingDirectorStateKey(v), z71.string().regex(STATE_KEY_PATTERN).nullable()),
16005
+ usesPrimary: z71.boolean().catch(false),
16006
+ person: z71.preprocess((v) => v === void 0 ? null : v, PersonSchema.nullable()),
16007
+ scene: lenientText(C.scene),
16008
+ panels: lenientList(PanelSchema, C.panels),
16009
+ textToRender: z71.preprocess((v) => v && typeof v === "object" && !Array.isArray(v) ? v : {}, LandingDirectorTextSchema),
16010
+ props: lenientTextList(C.props, C.propChars),
16011
+ mustNotShow: lenientTextList(C.mustNotShow, C.mustNotShowChars)
16012
+ });
16013
+ var LandingDirectorOutputSchema = z71.object({
16014
+ version: z71.literal(LANDING_DIRECTOR_SCHEMA_VERSION),
16015
+ confidence: z71.number().min(0).max(1),
16016
+ global: LandingDirectorGlobalSchema,
16017
+ sections: z71.array(LandingDirectorSectionSchema)
16018
+ });
16019
+ var StoredLandingDirectorSchema = z71.object({
16020
+ version: z71.literal(LANDING_DIRECTOR_SCHEMA_VERSION),
16021
+ arm: z71.literal(LANDING_DIRECTOR_ARM),
16022
+ modelId: z71.string().min(1),
16023
+ generatedAt: z71.string().min(1),
16024
+ confidence: z71.number().min(0).max(1),
16025
+ /** The plan the answer was validated against (sectionKey, or the role on classic). */
16026
+ planSectionKeys: z71.array(z71.string().min(1)),
16027
+ output: LandingDirectorOutputSchema,
16028
+ /** Sections that fell back to the category composition, and why. */
16029
+ fallbackSections: z71.array(z71.object({
16030
+ sectionKey: z71.string().min(1),
16031
+ reason: z71.string().min(1)
16032
+ }))
16033
+ });
16034
+
16035
+ // ../../packages/types/src/flight-recorder.ts
16036
+ import { z as z72 } from "zod";
16037
+ var FlightRecorderGenerationTypeSchema = z72.enum([
15185
16038
  "initial",
15186
16039
  "regeneration",
15187
16040
  "edit",
15188
16041
  "retry"
15189
16042
  ]);
15190
- var FlightRecorderProviderSchema = z68.enum([
16043
+ var FlightRecorderProviderSchema = z72.enum([
15191
16044
  "kie",
15192
16045
  "fal"
15193
16046
  ]);
15194
- var FlightRecorderElementSchema = z68.object({
15195
- name: z68.string().optional(),
15196
- description: z68.string().optional(),
15197
- frontalImageUrl: z68.string().optional(),
15198
- referenceImageUrls: z68.array(z68.string()).optional(),
15199
- videoUrl: z68.string().optional()
15200
- });
15201
- var FlightRecorderDistillationSchema = z68.object({
15202
- actorGender: z68.enum([
16047
+ var FlightRecorderElementSchema = z72.object({
16048
+ name: z72.string().optional(),
16049
+ description: z72.string().optional(),
16050
+ frontalImageUrl: z72.string().optional(),
16051
+ referenceImageUrls: z72.array(z72.string()).optional(),
16052
+ videoUrl: z72.string().optional()
16053
+ });
16054
+ var FlightRecorderDistillationSchema = z72.object({
16055
+ actorGender: z72.enum([
15203
16056
  "female",
15204
16057
  "male",
15205
16058
  "mixed"
15206
- ]).or(z68.string()).optional(),
15207
- voiceId: z68.string().optional(),
15208
- cameraMotion: z68.string().optional()
15209
- }).catchall(z68.unknown());
15210
- var FlightRecorderEntrySchema = z68.object({
15211
- schemaVersion: z68.literal(1).optional(),
15212
- id: z68.string().min(1),
15213
- taskId: z68.string().min(1),
15214
- runId: z68.string().min(1),
15215
- sequence: z68.number().int().nonnegative(),
15216
- timestamp: z68.string().min(1),
15217
- provider: FlightRecorderProviderSchema.or(z68.string()),
15218
- model: z68.string().min(1),
15219
- prompt: z68.string(),
15220
- durationSeconds: z68.number().nonnegative(),
15221
- aspectRatio: z68.string().optional(),
15222
- resolution: z68.string().optional(),
15223
- startImageUrl: z68.string().optional(),
15224
- endImageUrl: z68.string().optional(),
15225
- generateAudio: z68.boolean().optional(),
16059
+ ]).or(z72.string()).optional(),
16060
+ voiceId: z72.string().optional(),
16061
+ cameraMotion: z72.string().optional()
16062
+ }).catchall(z72.unknown());
16063
+ var FlightRecorderEntrySchema = z72.object({
16064
+ schemaVersion: z72.literal(1).optional(),
16065
+ id: z72.string().min(1),
16066
+ taskId: z72.string().min(1),
16067
+ runId: z72.string().min(1),
16068
+ sequence: z72.number().int().nonnegative(),
16069
+ timestamp: z72.string().min(1),
16070
+ provider: FlightRecorderProviderSchema.or(z72.string()),
16071
+ model: z72.string().min(1),
16072
+ prompt: z72.string(),
16073
+ durationSeconds: z72.number().nonnegative(),
16074
+ aspectRatio: z72.string().optional(),
16075
+ resolution: z72.string().optional(),
16076
+ startImageUrl: z72.string().optional(),
16077
+ endImageUrl: z72.string().optional(),
16078
+ generateAudio: z72.boolean().optional(),
15226
16079
  // Correlation & pipeline metadata
15227
- angleKey: z68.string().optional(),
15228
- sceneIndex: z68.number().int().nonnegative().optional(),
16080
+ angleKey: z72.string().optional(),
16081
+ sceneIndex: z72.number().int().nonnegative().optional(),
15229
16082
  generationType: FlightRecorderGenerationTypeSchema,
15230
- operationId: z68.string().min(1).optional(),
15231
- revisionId: z68.string().min(1).optional(),
15232
- attemptNumber: z68.number().int().nonnegative().default(1),
16083
+ operationId: z72.string().min(1).optional(),
16084
+ revisionId: z72.string().min(1).optional(),
16085
+ attemptNumber: z72.number().int().nonnegative().default(1),
15233
16086
  // Elements & Distillation
15234
- elements: z68.array(FlightRecorderElementSchema).optional(),
16087
+ elements: z72.array(FlightRecorderElementSchema).optional(),
15235
16088
  distillation: FlightRecorderDistillationSchema.optional(),
15236
16089
  // Raw payload (sanitized of secrets)
15237
- rawPayload: z68.record(z68.unknown())
16090
+ rawPayload: z72.record(z72.unknown())
15238
16091
  });
15239
- var FlightRecorderQuerySchema = z68.object({
15240
- taskId: z68.string().min(1),
15241
- runId: z68.string().optional(),
15242
- sceneIndex: z68.number().int().nonnegative().optional(),
15243
- angleKey: z68.string().optional(),
16092
+ var FlightRecorderQuerySchema = z72.object({
16093
+ taskId: z72.string().min(1),
16094
+ runId: z72.string().optional(),
16095
+ sceneIndex: z72.number().int().nonnegative().optional(),
16096
+ angleKey: z72.string().optional(),
15244
16097
  generationType: FlightRecorderGenerationTypeSchema.optional(),
15245
- cursor: z68.number().int().nonnegative().optional(),
15246
- limit: z68.number().int().positive().optional()
16098
+ cursor: z72.number().int().nonnegative().optional(),
16099
+ limit: z72.number().int().positive().optional()
15247
16100
  });
15248
- var FlightRecorderResponseSchema = z68.object({
15249
- taskId: z68.string().min(1),
15250
- runId: z68.string().min(1),
15251
- entries: z68.array(FlightRecorderEntrySchema),
16101
+ var FlightRecorderResponseSchema = z72.object({
16102
+ taskId: z72.string().min(1),
16103
+ runId: z72.string().min(1),
16104
+ entries: z72.array(FlightRecorderEntrySchema),
15252
16105
  lastCall: FlightRecorderEntrySchema.nullable(),
15253
- count: z68.number().int().nonnegative(),
15254
- nextCursor: z68.number().int().nonnegative().nullable()
16106
+ count: z72.number().int().nonnegative(),
16107
+ nextCursor: z72.number().int().nonnegative().nullable()
15255
16108
  });
15256
16109
 
15257
16110
  // src/http.ts
@@ -15354,6 +16207,9 @@ function buildProxyUrl(opts) {
15354
16207
  }
15355
16208
  __name(buildProxyUrl, "buildProxyUrl");
15356
16209
 
16210
+ // src/http.ts
16211
+ import { getOAuthProtectedResourceMetadataUrl as getOAuthProtectedResourceMetadataUrl2 } from "@modelcontextprotocol/sdk/server/auth/router.js";
16212
+
15357
16213
  // src/auth/http-oauth.ts
15358
16214
  import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
15359
16215
  import { getOAuthProtectedResourceMetadataUrl, mcpAuthMetadataRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
@@ -15426,8 +16282,8 @@ async function readResponseText(response, maxBytes) {
15426
16282
  }
15427
16283
  __name(readResponseText, "readResponseText");
15428
16284
  async function readResponseJson(response, maxBytes) {
15429
- const text = await readResponseText(response, maxBytes);
15430
- return JSON.parse(text);
16285
+ const text2 = await readResponseText(response, maxBytes);
16286
+ return JSON.parse(text2);
15431
16287
  }
15432
16288
  __name(readResponseJson, "readResponseJson");
15433
16289
 
@@ -15760,22 +16616,23 @@ import { InsufficientScopeError, InvalidTokenError } from "@modelcontextprotocol
15760
16616
  import { createRemoteJWKSet as createRemoteJWKSet2, jwtVerify as jwtVerify2 } from "jose";
15761
16617
 
15762
16618
  // src/auth/oauth-config.ts
15763
- import { z as z69 } from "zod";
16619
+ import { z as z73 } from "zod";
15764
16620
  var MCP_RESOURCE = new URL("https://mcp.productmaker.app/mcp");
15765
16621
  var MCP_SCOPE = "mcp:access";
16622
+ var REFRESH_SCOPE = "offline_access";
15766
16623
  var STATIC_CLIENT_ISSUER = new URL(MCP_RESOURCE.origin);
15767
- var oauthEnvSchema = z69.object({
15768
- MCP_REMOTE_AUTH_MODE: z69.literal("oauth"),
15769
- MCP_AUTH0_ISSUER: z69.string().trim().min(1),
15770
- MCP_AUTH0_AUDIENCE: z69.string().trim().refine((value) => value === MCP_RESOURCE.href, {
16624
+ var oauthEnvSchema = z73.object({
16625
+ MCP_REMOTE_AUTH_MODE: z73.literal("oauth"),
16626
+ MCP_AUTH0_ISSUER: z73.string().trim().min(1),
16627
+ MCP_AUTH0_AUDIENCE: z73.string().trim().refine((value) => value === MCP_RESOURCE.href, {
15771
16628
  message: `must equal ${MCP_RESOURCE.href}`
15772
16629
  }),
15773
- MCP_AUTH0_CLIENT_ID: z69.string().trim().min(1),
15774
- MCP_AUTH0_CLIENT_SECRET: z69.string().trim().min(1),
15775
- PM_API_AUDIENCE: z69.string().trim().min(1),
15776
- AUTH0_AUDIENCE: z69.string().trim().min(1),
15777
- MCP_STATIC_CLIENT_ID: z69.string().trim().min(1).optional(),
15778
- MCP_STATIC_CLIENT_SECRET: z69.string().trim().min(1).optional()
16630
+ MCP_AUTH0_CLIENT_ID: z73.string().trim().min(1),
16631
+ MCP_AUTH0_CLIENT_SECRET: z73.string().trim().min(1),
16632
+ PM_API_AUDIENCE: z73.string().trim().min(1),
16633
+ AUTH0_AUDIENCE: z73.string().trim().min(1),
16634
+ MCP_STATIC_CLIENT_ID: z73.string().trim().min(1).optional(),
16635
+ MCP_STATIC_CLIENT_SECRET: z73.string().trim().min(1).optional()
15779
16636
  });
15780
16637
  function formatZodIssues(error) {
15781
16638
  return error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
@@ -16059,7 +16916,7 @@ function badRequest(res, description) {
16059
16916
  __name(badRequest, "badRequest");
16060
16917
  function createOAuthProxyRouter(options) {
16061
16918
  const router = express.Router();
16062
- const stateKey = deriveStateKey(options.staticClient.clientSecret);
16919
+ const stateKey3 = deriveStateKey(options.staticClient.clientSecret);
16063
16920
  const now = options.now ?? (() => Date.now());
16064
16921
  router.get(OAUTH_PROXY_PATHS.authorize, (req, res) => {
16065
16922
  const redirectUri = firstQueryValue(req.query.redirect_uri);
@@ -16088,7 +16945,7 @@ function createOAuthProxyRouter(options) {
16088
16945
  }
16089
16946
  }
16090
16947
  upstream.searchParams.set("redirect_uri", PROXY_CALLBACK_URL);
16091
- upstream.searchParams.set("state", sealState(stateKey, {
16948
+ upstream.searchParams.set("state", sealState(stateKey3, {
16092
16949
  redirectUri,
16093
16950
  ...firstQueryValue(req.query.state) === void 0 ? {} : {
16094
16951
  clientState: firstQueryValue(req.query.state)
@@ -16098,7 +16955,7 @@ function createOAuthProxyRouter(options) {
16098
16955
  });
16099
16956
  router.get(OAUTH_PROXY_PATHS.callback, (req, res) => {
16100
16957
  const sealed = firstQueryValue(req.query.state);
16101
- const opened = sealed === void 0 ? void 0 : openState(stateKey, sealed, now());
16958
+ const opened = sealed === void 0 ? void 0 : openState(stateKey3, sealed, now());
16102
16959
  if (!opened) {
16103
16960
  badRequest(res, "state is missing, tampered with, or expired");
16104
16961
  return;
@@ -16146,13 +17003,10 @@ function createOAuthProxyRouter(options) {
16146
17003
  });
16147
17004
  return;
16148
17005
  }
17006
+ body.set("client_secret", options.staticClient.clientSecret);
16149
17007
  const headers = {
16150
17008
  "content-type": "application/x-www-form-urlencoded"
16151
17009
  };
16152
- const authorization = req.headers.authorization;
16153
- if (typeof authorization === "string") {
16154
- headers.authorization = authorization;
16155
- }
16156
17010
  let upstream;
16157
17011
  let payload;
16158
17012
  try {
@@ -16185,11 +17039,12 @@ var REGISTRATION_RATE_LIMIT = {
16185
17039
  windowMs: 6e4,
16186
17040
  max: 120
16187
17041
  };
17042
+ var PUBLIC_CLIENT_SECRET_PLACEHOLDER = "pm-public-client";
16188
17043
  function buildStaticClientInformation(staticClient, requested) {
16189
17044
  return {
16190
17045
  client_id: staticClient.clientId,
16191
- client_secret: staticClient.clientSecret,
16192
- // 0 = never expires (RFC 7591 §3.2.1); the secret is Auth0's, not ours.
17046
+ client_secret: PUBLIC_CLIENT_SECRET_PLACEHOLDER,
17047
+ // 0 = never expires (RFC 7591 §3.2.1); the placeholder is a constant.
16193
17048
  client_secret_expires_at: 0,
16194
17049
  token_endpoint_auth_method: "client_secret_post",
16195
17050
  redirect_uris: requested.redirect_uris,
@@ -16200,7 +17055,12 @@ function buildStaticClientInformation(staticClient, requested) {
16200
17055
  response_types: [
16201
17056
  "code"
16202
17057
  ],
16203
- scope: requested.scope ?? MCP_SCOPE,
17058
+ // Clients that defer scope choice to the server get the full intended
17059
+ // set: `mcp:access` for the resource plus `offline_access` so the session
17060
+ // can refresh instead of dying an interactive re-auth every 900s access
17061
+ // token (the auth loop seen in production). A client that names its own
17062
+ // scope list is echoed verbatim — its choice wins.
17063
+ scope: requested.scope ?? `${MCP_SCOPE} ${REFRESH_SCOPE}`,
16204
17064
  ...requested.client_name !== void 0 ? {
16205
17065
  client_name: requested.client_name
16206
17066
  } : {}
@@ -16247,8 +17107,12 @@ function buildStaticClientAuthorizationServerMetadata(auth0Metadata) {
16247
17107
  token_endpoint_auth_methods_supported: [
16248
17108
  "client_secret_post"
16249
17109
  ],
17110
+ // `offline_access` is what lets a client ASK for a refresh token; without
17111
+ // it in scopes_supported, conforming clients never request it and every
17112
+ // 900s access-token expiry becomes an interactive re-auth loop.
16250
17113
  scopes_supported: [
16251
- MCP_SCOPE
17114
+ MCP_SCOPE,
17115
+ REFRESH_SCOPE
16252
17116
  ],
16253
17117
  service_documentation: "https://productmaker.app/"
16254
17118
  };
@@ -16760,7 +17624,10 @@ var ApiClient = class {
16760
17624
  method: "POST",
16761
17625
  headers: {
16762
17626
  ...authHeaders,
16763
- "Content-Type": "application/json"
17627
+ "Content-Type": "application/json",
17628
+ ...options?.idempotencyKey ? {
17629
+ "Idempotency-Key": options.idempotencyKey
17630
+ } : {}
16764
17631
  },
16765
17632
  body: JSON.stringify(body)
16766
17633
  }, options);
@@ -16882,7 +17749,7 @@ var ApiClient = class {
16882
17749
  };
16883
17750
 
16884
17751
  // src/index.ts
16885
- var MCP_VERSION = "8.1.0";
17752
+ var MCP_VERSION = "8.3.0";
16886
17753
 
16887
17754
  // src/meta-upload-app.ts
16888
17755
  import { readFileSync } from "fs";
@@ -16954,7 +17821,7 @@ function registerMediaUploadAppResource(server, resolveEndpoint) {
16954
17821
  __name(registerMediaUploadAppResource, "registerMediaUploadAppResource");
16955
17822
 
16956
17823
  // src/tools/tasks.ts
16957
- import { z as z73 } from "zod";
17824
+ import { z as z77 } from "zod";
16958
17825
 
16959
17826
  // src/resolve-image.ts
16960
17827
  import { lookup as dnsLookup } from "dns/promises";
@@ -17515,10 +18382,10 @@ async function resolveMediaRef(ref, opts) {
17515
18382
  __name(resolveMediaRef, "resolveMediaRef");
17516
18383
 
17517
18384
  // src/tools/shared.ts
17518
- import { z as z70 } from "zod";
17519
- var Lang = z70.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);
17520
- var Country = z70.string().min(2).max(3);
17521
- var LandingStrategyEnum = z70.enum([
18385
+ import { z as z74 } from "zod";
18386
+ var Lang = z74.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);
18387
+ var Country = z74.string().min(2).max(3);
18388
+ var LandingStrategyEnum = z74.enum([
17522
18389
  "auto",
17523
18390
  "classic",
17524
18391
  "pain_solution",
@@ -17527,12 +18394,12 @@ var LandingStrategyEnum = z70.enum([
17527
18394
  "offer_value",
17528
18395
  "lifestyle_identity"
17529
18396
  ]);
17530
- var LandingImageFormatEnum = z70.enum([
18397
+ var LandingImageFormatEnum = z74.enum([
17531
18398
  "9:16",
17532
18399
  "4:5",
17533
18400
  "1:1"
17534
18401
  ]);
17535
- var VideoPresetEnum = z70.enum([
18402
+ var VideoPresetEnum = z74.enum([
17536
18403
  "auto",
17537
18404
  "ugc_dynamic",
17538
18405
  "ugc_how_to",
@@ -17545,21 +18412,21 @@ var VideoPresetEnum = z70.enum([
17545
18412
  "pixar_problem",
17546
18413
  "pixar_sassy"
17547
18414
  ]);
17548
- var VideoStructureEnum = z70.enum([
18415
+ var VideoStructureEnum = z74.enum([
17549
18416
  "single_take",
17550
18417
  "multi_scene"
17551
18418
  ]);
17552
- var VideoQualityEnum = z70.enum([
18419
+ var VideoQualityEnum = z74.enum([
17553
18420
  "economy",
17554
18421
  "medium",
17555
18422
  "pro"
17556
18423
  ]);
17557
- var VideoRouteIdEnum = z70.enum([
18424
+ var VideoRouteIdEnum = z74.enum([
17558
18425
  "single_take",
17559
18426
  "multi_scene",
17560
18427
  "free"
17561
18428
  ]);
17562
- var TaskCreativeVariantVideoPresetEnum = z70.enum([
18429
+ var TaskCreativeVariantVideoPresetEnum = z74.enum([
17563
18430
  "auto",
17564
18431
  "ugc_dynamic",
17565
18432
  "ugc_how_to",
@@ -17576,53 +18443,43 @@ var VIDEO_PRESET_RESOLUTION = {
17576
18443
  auto: null,
17577
18444
  ugc_dynamic: {
17578
18445
  videoStyle: "ai_dynamic",
17579
- narrativeStyle: "review",
17580
- videoModel: "veo"
18446
+ narrativeStyle: "review"
17581
18447
  },
17582
18448
  ugc_how_to: {
17583
18449
  videoStyle: "ai_dynamic",
17584
- narrativeStyle: "ugc_how_to",
17585
- videoModel: "veo"
18450
+ narrativeStyle: "ugc_how_to"
17586
18451
  },
17587
18452
  ugc_unboxing_chaos: {
17588
18453
  videoStyle: "ai_dynamic",
17589
- narrativeStyle: "ugc_unboxing_chaos",
17590
- videoModel: "veo"
18454
+ narrativeStyle: "ugc_unboxing_chaos"
17591
18455
  },
17592
18456
  problem_solution: {
17593
18457
  videoStyle: "ai_dynamic",
17594
- narrativeStyle: "problem_product_cta",
17595
- videoModel: "kling"
18458
+ narrativeStyle: "problem_product_cta"
17596
18459
  },
17597
18460
  pixar_problem: {
17598
18461
  videoStyle: "ai_dynamic",
17599
- narrativeStyle: "animated_problem_pixar",
17600
- videoModel: "kling"
18462
+ narrativeStyle: "animated_problem_pixar"
17601
18463
  },
17602
18464
  pixar_sassy: {
17603
18465
  videoStyle: "ai_dynamic",
17604
- narrativeStyle: "sassy_product_pixar",
17605
- videoModel: "seedance"
18466
+ narrativeStyle: "sassy_product_pixar"
17606
18467
  },
17607
18468
  product_showcase: {
17608
18469
  videoStyle: "product_only",
17609
- narrativeStyle: "product_showcase",
17610
- videoModel: "veo"
18470
+ narrativeStyle: "product_showcase"
17611
18471
  },
17612
18472
  wearable_showcase: {
17613
18473
  videoStyle: "ugc",
17614
- narrativeStyle: "wearable_showcase",
17615
- videoModel: "veo"
18474
+ narrativeStyle: "wearable_showcase"
17616
18475
  },
17617
18476
  cinematic: {
17618
18477
  videoStyle: "product_only",
17619
- narrativeStyle: "cinematic",
17620
- videoModel: "grok"
18478
+ narrativeStyle: "cinematic"
17621
18479
  },
17622
18480
  wild_card: {
17623
18481
  videoStyle: "product_only",
17624
- narrativeStyle: "wild_card",
17625
- videoModel: "grok"
18482
+ narrativeStyle: "wild_card"
17626
18483
  }
17627
18484
  };
17628
18485
  function resolveVideoPreset(preset) {
@@ -17630,7 +18487,7 @@ function resolveVideoPreset(preset) {
17630
18487
  return VIDEO_PRESET_RESOLUTION[preset] ?? null;
17631
18488
  }
17632
18489
  __name(resolveVideoPreset, "resolveVideoPreset");
17633
- var VideoTierEnum = z70.enum([
18490
+ var VideoTierEnum = z74.enum([
17634
18491
  "basic"
17635
18492
  ]);
17636
18493
  function resolveVideoTier(_tier) {
@@ -17670,22 +18527,22 @@ function assertSemanticVideoDurationCompatibility(structure, quality, durationSe
17670
18527
  }
17671
18528
  }
17672
18529
  __name(assertSemanticVideoDurationCompatibility, "assertSemanticVideoDurationCompatibility");
17673
- var VideoResolutionEnum = z70.enum([
18530
+ var VideoResolutionEnum = z74.enum([
17674
18531
  "480p",
17675
18532
  "720p"
17676
18533
  ]);
17677
- var CreativeGenderEnum = z70.enum([
18534
+ var CreativeGenderEnum = z74.enum([
17678
18535
  "female",
17679
18536
  "male",
17680
18537
  "mixed"
17681
18538
  ]);
17682
- var VideoStyleEnum = z70.enum([
18539
+ var VideoStyleEnum = z74.enum([
17683
18540
  "ugc",
17684
18541
  "cartoon_3d",
17685
18542
  "product_only",
17686
18543
  "ai_dynamic"
17687
18544
  ]);
17688
- var NarrativeStyleEnum = z70.enum([
18545
+ var NarrativeStyleEnum = z74.enum([
17689
18546
  "review",
17690
18547
  "ugc_how_to",
17691
18548
  "problem_product_cta",
@@ -17703,7 +18560,7 @@ var NarrativeStyleEnum = z70.enum([
17703
18560
  "ugc_demo_review",
17704
18561
  "market"
17705
18562
  ]);
17706
- var PublicNarrativeStyleEnum = z70.enum([
18563
+ var PublicNarrativeStyleEnum = z74.enum([
17707
18564
  "review",
17708
18565
  "ugc_how_to",
17709
18566
  "ugc_unboxing_chaos",
@@ -17716,7 +18573,7 @@ var PublicNarrativeStyleEnum = z70.enum([
17716
18573
  "sassy_product_pixar",
17717
18574
  "market"
17718
18575
  ]);
17719
- var VideoModelEnum = z70.enum([
18576
+ var TaskVideoModelEnum = z74.enum([
17720
18577
  "veo",
17721
18578
  "wan",
17722
18579
  "bytedance",
@@ -17724,29 +18581,29 @@ var VideoModelEnum = z70.enum([
17724
18581
  "grok",
17725
18582
  "kling"
17726
18583
  ]);
17727
- var ProductReferenceUserChoiceEnum = z70.enum([
18584
+ var ProductReferenceUserChoiceEnum = z74.enum([
17728
18585
  "same_product",
17729
18586
  "variants",
17730
18587
  "different_products",
17731
18588
  "combo_distinct"
17732
18589
  ]);
17733
- var ProductReferenceCandidateGroupingEnum = z70.enum([
18590
+ var ProductReferenceCandidateGroupingEnum = z74.enum([
17734
18591
  "same_product_same_variant",
17735
18592
  "same_product_different_variants",
17736
18593
  "different_products",
17737
18594
  "unclear"
17738
18595
  ]);
17739
- var ProductReferenceScopeEnum = z70.enum([
18596
+ var ProductReferenceScopeEnum = z74.enum([
17740
18597
  "base",
17741
18598
  "video_view_pack"
17742
18599
  ]);
17743
- var ProductReferenceResolutionDecisionEnum = z70.enum([
18600
+ var ProductReferenceResolutionDecisionEnum = z74.enum([
17744
18601
  "approve",
17745
18602
  "reject",
17746
18603
  "observed_only",
17747
18604
  "correct_view"
17748
18605
  ]);
17749
- var ProductReferenceRejectionReasonEnum = z70.enum([
18606
+ var ProductReferenceRejectionReasonEnum = z74.enum([
17750
18607
  "replacement_requested",
17751
18608
  "wrong_product_identity",
17752
18609
  "wrong_variant",
@@ -17757,13 +18614,13 @@ var ProductReferenceRejectionReasonEnum = z70.enum([
17757
18614
  "not_a_product_photo",
17758
18615
  "other"
17759
18616
  ]);
17760
- var GapDurationChoiceMirror = z70.object({
17761
- angleIndex: z70.number().int().nonnegative(),
17762
- recommendedSec: z70.union([
17763
- z70.literal(15),
17764
- z70.literal(30)
18617
+ var GapDurationChoiceMirror = z74.object({
18618
+ angleIndex: z74.number().int().nonnegative(),
18619
+ recommendedSec: z74.union([
18620
+ z74.literal(15),
18621
+ z74.literal(30)
17765
18622
  ]),
17766
- estimatedDeltaCredits: z70.number().int().nonnegative().optional()
18623
+ estimatedDeltaCredits: z74.number().int().nonnegative().optional()
17767
18624
  });
17768
18625
  function buildSelectedImageUrlsHash(urls) {
17769
18626
  const input = [
@@ -17773,13 +18630,13 @@ function buildSelectedImageUrlsHash(urls) {
17773
18630
  return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
17774
18631
  }
17775
18632
  __name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
17776
- var ImageCreativeStyleEnum = z70.enum([
18633
+ var ImageCreativeStyleEnum = z74.enum([
17777
18634
  "studio",
17778
18635
  "floating",
17779
18636
  "ingredients",
17780
18637
  "in_use"
17781
18638
  ]);
17782
- var ImageCreativeArchetypeEnum = z70.enum([
18639
+ var ImageCreativeArchetypeEnum = z74.enum([
17783
18640
  "direct_response_product_ad",
17784
18641
  "hero_outcome",
17785
18642
  "offer_value_prop",
@@ -17788,20 +18645,20 @@ var ImageCreativeArchetypeEnum = z70.enum([
17788
18645
  "problem_solution",
17789
18646
  "lifestyle_identity"
17790
18647
  ]);
17791
- var ImageCreativeVisualTreatmentEnum = z70.enum([
18648
+ var ImageCreativeVisualTreatmentEnum = z74.enum([
17792
18649
  "performance_product_ad",
17793
18650
  "premium_dtc_carousel"
17794
18651
  ]);
17795
- var ImageCreativeAspectRatioEnum = z70.enum([
18652
+ var ImageCreativeAspectRatioEnum = z74.enum([
17796
18653
  "1:1",
17797
18654
  "4:5",
17798
18655
  "9:16",
17799
18656
  "16:9"
17800
18657
  ]);
17801
- var ImageRefSchema = z70.object({
17802
- url: z70.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.'),
17803
- path: z70.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`.'),
17804
- assetId: z70.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.")
18658
+ var ImageRefSchema = z74.object({
18659
+ url: z74.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.'),
18660
+ path: z74.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`.'),
18661
+ assetId: z74.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.")
17805
18662
  }).refine((v) => [
17806
18663
  v.url,
17807
18664
  v.path,
@@ -17809,11 +18666,11 @@ var ImageRefSchema = z70.object({
17809
18666
  ].filter(Boolean).length === 1, {
17810
18667
  message: "each image entry must have exactly one of `url`, `path`, or `assetId`"
17811
18668
  });
17812
- var ImagesInput = z70.array(ImageRefSchema).min(1).max(5).superRefine((images, ctx) => {
18669
+ var ImagesInput = z74.array(ImageRefSchema).min(1).max(5).superRefine((images, ctx) => {
17813
18670
  const assetCount = images.filter((image) => Boolean(image.assetId)).length;
17814
18671
  if (assetCount > 0 && assetCount !== images.length) {
17815
18672
  ctx.addIssue({
17816
- code: z70.ZodIssueCode.custom,
18673
+ code: z74.ZodIssueCode.custom,
17817
18674
  message: "all image entries must use assetId, or all must use URL/path sources"
17818
18675
  });
17819
18676
  }
@@ -17876,19 +18733,24 @@ async function submitCreative(api, opts) {
17876
18733
  const taskId = r.taskId ?? r.jobId;
17877
18734
  return asText({
17878
18735
  taskId,
17879
- status: "queued",
17880
- estimatedSeconds: opts.estimatedSeconds
18736
+ // Replay-aware: a keyed retry may land on a task that is already
18737
+ // processing/done — report the real status, not a hopeful "queued".
18738
+ status: r.status ?? "queued",
18739
+ estimatedSeconds: opts.estimatedSeconds,
18740
+ ...r.replayed === true ? {
18741
+ replayed: true
18742
+ } : {}
17881
18743
  });
17882
18744
  }
17883
18745
  __name(submitCreative, "submitCreative");
17884
- var ResearchTargetCountryEnum = z70.enum([
18746
+ var ResearchTargetCountryEnum = z74.enum([
17885
18747
  "CO",
17886
18748
  "MX",
17887
18749
  "PE",
17888
18750
  "EC",
17889
18751
  "CL"
17890
18752
  ]);
17891
- var ResearchCategoryEnum = z70.enum([
18753
+ var ResearchCategoryEnum = z74.enum([
17892
18754
  "auto",
17893
18755
  "automotive",
17894
18756
  "kitchen",
@@ -17957,7 +18819,7 @@ var OVERWRITES_EXTERNAL = {
17957
18819
  idempotentHint: false,
17958
18820
  openWorldHint: false
17959
18821
  };
17960
- var DailyControlStatusEnum = z70.enum([
18822
+ var DailyControlStatusEnum = z74.enum([
17961
18823
  "ok",
17962
18824
  "sales_unavailable",
17963
18825
  "timezone_unavailable",
@@ -17970,19 +18832,19 @@ var DailyControlStatusEnum = z70.enum([
17970
18832
  "product_missing",
17971
18833
  "store_unreachable"
17972
18834
  ]);
17973
- var DailyControlLightEnum = z70.enum([
18835
+ var DailyControlLightEnum = z74.enum([
17974
18836
  "green",
17975
18837
  "amber",
17976
18838
  "red",
17977
18839
  "none"
17978
18840
  ]);
17979
- var DailyControlVerdictEnum = z70.enum([
18841
+ var DailyControlVerdictEnum = z74.enum([
17980
18842
  "scale",
17981
18843
  "hold",
17982
18844
  "kill",
17983
18845
  "insufficient_sample"
17984
18846
  ]);
17985
- var DailyControlSourceEnum = z70.enum([
18847
+ var DailyControlSourceEnum = z74.enum([
17986
18848
  "shopify",
17987
18849
  "shopify_settling",
17988
18850
  "user",
@@ -19070,29 +19932,29 @@ ${parsed.data.error ? `${escapeMarkdownText(parsed.data.error)}
19070
19932
  __name(renderVideoScriptMarkdown, "renderVideoScriptMarkdown");
19071
19933
 
19072
19934
  // src/tools/media-presentation.ts
19073
- import { z as z71 } from "zod";
19074
- var HttpsPresentationUrlSchema = z71.string().url().max(2048).refine((value) => new URL(value).protocol === "https:");
19935
+ import { z as z75 } from "zod";
19936
+ var HttpsPresentationUrlSchema = z75.string().url().max(2048).refine((value) => new URL(value).protocol === "https:");
19075
19937
  var ProductMakerPreviewUrlSchema = HttpsPresentationUrlSchema.refine((value) => {
19076
19938
  const hostname = new URL(value).hostname.toLowerCase();
19077
19939
  return hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
19078
19940
  }, "preview URL must be owned by ProductMaker");
19079
- var positiveIntAtMost = /* @__PURE__ */ __name((maximum) => z71.number().int().positive().max(maximum), "positiveIntAtMost");
19080
- var PresentationImageSchema = z71.object({
19081
- kind: z71.literal("image"),
19941
+ var positiveIntAtMost = /* @__PURE__ */ __name((maximum) => z75.number().int().positive().max(maximum), "positiveIntAtMost");
19942
+ var PresentationImageSchema = z75.object({
19943
+ kind: z75.literal("image"),
19082
19944
  url: HttpsPresentationUrlSchema,
19083
- mimeType: z71.enum([
19945
+ mimeType: z75.enum([
19084
19946
  "image/jpeg",
19085
19947
  "image/png"
19086
19948
  ]),
19087
19949
  byteSize: positiveIntAtMost(5 * 1024 * 1024),
19088
19950
  width: positiveIntAtMost(16384),
19089
19951
  height: positiveIntAtMost(16384),
19090
- alt: z71.string().trim().min(1).max(240)
19952
+ alt: z75.string().trim().min(1).max(240)
19091
19953
  }).strict();
19092
19954
  var McpMediaPresentationSchema = PresentationImageSchema;
19093
- var GalleryImageSchema = z71.object({
19955
+ var GalleryImageSchema = z75.object({
19094
19956
  url: ProductMakerPreviewUrlSchema,
19095
- alt: z71.string().trim().min(1).max(240)
19957
+ alt: z75.string().trim().min(1).max(240)
19096
19958
  });
19097
19959
  var MCP_GALLERY_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
19098
19960
  async function imageBlocks(image, transport) {
@@ -19241,11 +20103,175 @@ ${summary}`,
19241
20103
  }
19242
20104
  __name(requirePaidCreativeConfirmation, "requirePaidCreativeConfirmation");
19243
20105
 
20106
+ // src/tools/stable-uuid.ts
20107
+ import { createHash as createHash3 } from "crypto";
20108
+ function canonicalize(value) {
20109
+ if (Array.isArray(value)) return value.map(canonicalize);
20110
+ if (value === null || typeof value !== "object") return value;
20111
+ const record = value;
20112
+ return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
20113
+ key,
20114
+ canonicalize(record[key])
20115
+ ]));
20116
+ }
20117
+ __name(canonicalize, "canonicalize");
20118
+ function canonicalJson(value) {
20119
+ return JSON.stringify(canonicalize(value));
20120
+ }
20121
+ __name(canonicalJson, "canonicalJson");
20122
+ function deriveUuidV5(name, namespace) {
20123
+ const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
20124
+ const digest = createHash3("sha1").update(namespaceBytes).update(name, "utf8").digest();
20125
+ const bytes = Buffer.from(digest.subarray(0, 16));
20126
+ bytes[6] = (bytes[6] ?? 0) & 15 | 80;
20127
+ bytes[8] = (bytes[8] ?? 0) & 63 | 128;
20128
+ const hex = bytes.toString("hex");
20129
+ return [
20130
+ hex.slice(0, 8),
20131
+ hex.slice(8, 12),
20132
+ hex.slice(12, 16),
20133
+ hex.slice(16, 20),
20134
+ hex.slice(20)
20135
+ ].join("-");
20136
+ }
20137
+ __name(deriveUuidV5, "deriveUuidV5");
20138
+
20139
+ // src/tools/request-key.ts
20140
+ var PAID_REQUEST_KEY_WINDOW_MS = 15 * 60 * 1e3;
20141
+ var PAID_REQUEST_KEY_NAMESPACE = "8f3d6b1c-2a47-4e5b-9c0d-6e7f8a9b0c1d";
20142
+ var PARAPHRASE_PRONE_FIELDS = /* @__PURE__ */ new Set([
20143
+ "additionalContext",
20144
+ "productContext",
20145
+ "scriptNotes",
20146
+ "creativeDirection",
20147
+ "buyerPersona",
20148
+ "actorDescription",
20149
+ "description",
20150
+ "idempotencyKey",
20151
+ "clientRequestId"
20152
+ ]);
20153
+ var URL_FIELDS = /* @__PURE__ */ new Set([
20154
+ "landingUrl",
20155
+ "productUrl",
20156
+ "url",
20157
+ "serviceLandingUrl"
20158
+ ]);
20159
+ var TRACKING_PARAM_PATTERN = /^(utm_\w+|fbclid|gclid|msclkid|mc_cid|mc_eid|igshid)$/;
20160
+ var LANGUAGE_FIELDS = /* @__PURE__ */ new Set([
20161
+ "language",
20162
+ "lang"
20163
+ ]);
20164
+ var CASE_FOLD_FIELDS = /* @__PURE__ */ new Set([
20165
+ "country",
20166
+ "language",
20167
+ "lang"
20168
+ ]);
20169
+ var ORDER_INSIGNIFICANT_ARRAY_FIELDS = /* @__PURE__ */ new Set([
20170
+ "outputs",
20171
+ "selectedOutputs",
20172
+ "features"
20173
+ ]);
20174
+ function sortCanonical(items) {
20175
+ return [
20176
+ ...items
20177
+ ].sort((a, b) => {
20178
+ const ka = canonicalJson(a);
20179
+ const kb = canonicalJson(b);
20180
+ return ka < kb ? -1 : ka > kb ? 1 : 0;
20181
+ });
20182
+ }
20183
+ __name(sortCanonical, "sortCanonical");
20184
+ function isRecord3(value) {
20185
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20186
+ }
20187
+ __name(isRecord3, "isRecord");
20188
+ function collectLanguageCodes(value, out) {
20189
+ if (Array.isArray(value)) {
20190
+ const items = value;
20191
+ for (const item of items) collectLanguageCodes(item, out);
20192
+ return out;
20193
+ }
20194
+ if (!isRecord3(value)) return out;
20195
+ for (const [key, entry] of Object.entries(value)) {
20196
+ if (LANGUAGE_FIELDS.has(key) && typeof entry === "string") {
20197
+ const base = entry.trim().split(/[-_]/)[0]?.toLowerCase();
20198
+ if (base) out.add(base);
20199
+ }
20200
+ collectLanguageCodes(entry, out);
20201
+ }
20202
+ return out;
20203
+ }
20204
+ __name(collectLanguageCodes, "collectLanguageCodes");
20205
+ function normalizeSearchForKey(search) {
20206
+ if (!search) return "";
20207
+ const params = [
20208
+ ...new URLSearchParams(search).entries()
20209
+ ].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);
20210
+ if (!params.length) return "";
20211
+ const out = new URLSearchParams();
20212
+ for (const [key, value] of params) out.append(key, value);
20213
+ return `?${out.toString()}`;
20214
+ }
20215
+ __name(normalizeSearchForKey, "normalizeSearchForKey");
20216
+ function normalizeUrlForKey(value, languages) {
20217
+ const trimmed = value.trim();
20218
+ let url;
20219
+ try {
20220
+ url = new URL(trimmed);
20221
+ } catch {
20222
+ return trimmed.toLowerCase();
20223
+ }
20224
+ const host = url.hostname.toLowerCase().replace(/^www\./, "");
20225
+ const port = url.port ? `:${url.port}` : "";
20226
+ const segments = url.pathname.split("/").filter(Boolean);
20227
+ const soleSegment = segments.length === 1 ? segments[0] : void 0;
20228
+ if (soleSegment !== void 0 && languages.has(soleSegment.toLowerCase())) {
20229
+ segments.pop();
20230
+ }
20231
+ const path3 = segments.length ? `/${segments.join("/")}` : "";
20232
+ return host + port + path3 + normalizeSearchForKey(url.search);
20233
+ }
20234
+ __name(normalizeUrlForKey, "normalizeUrlForKey");
20235
+ function normalizeForKey(value, languages) {
20236
+ if (Array.isArray(value)) {
20237
+ const items = value;
20238
+ return items.map((item) => normalizeForKey(item, languages));
20239
+ }
20240
+ if (!isRecord3(value)) return value;
20241
+ const out = {};
20242
+ for (const [key, entry] of Object.entries(value)) {
20243
+ if (PARAPHRASE_PRONE_FIELDS.has(key)) {
20244
+ if (key === "description" && value.mode === "described") {
20245
+ out[key] = normalizeForKey(entry, languages);
20246
+ }
20247
+ continue;
20248
+ }
20249
+ out[key] = URL_FIELDS.has(key) && typeof entry === "string" ? normalizeUrlForKey(entry, languages) : CASE_FOLD_FIELDS.has(key) && typeof entry === "string" ? (
20250
+ // separator too or a host alternating between them splits the key.
20251
+ entry.trim().toLowerCase().replace(/_/g, "-")
20252
+ ) : ORDER_INSIGNIFICANT_ARRAY_FIELDS.has(key) && Array.isArray(entry) ? sortCanonical(entry.map((item) => normalizeForKey(item, languages))) : normalizeForKey(entry, languages);
20253
+ }
20254
+ return out;
20255
+ }
20256
+ __name(normalizeForKey, "normalizeForKey");
20257
+ function derivePaidRequestKey(args) {
20258
+ const languages = collectLanguageCodes(args.input, /* @__PURE__ */ new Set());
20259
+ const semanticInput = normalizeForKey(args.input, languages);
20260
+ const bucket = Math.floor(args.nowMs / PAID_REQUEST_KEY_WINDOW_MS);
20261
+ return deriveUuidV5(canonicalJson({
20262
+ user: args.fingerprint,
20263
+ tool: args.tool,
20264
+ input: semanticInput,
20265
+ bucket
20266
+ }), PAID_REQUEST_KEY_NAMESPACE);
20267
+ }
20268
+ __name(derivePaidRequestKey, "derivePaidRequestKey");
20269
+
19244
20270
  // src/tools/winning-product-image.ts
19245
- import { z as z72 } from "zod";
19246
- var WinnerReferenceInput = z72.object({
19247
- runId: z72.string().uuid(),
19248
- candidateSourceId: z72.string().trim().min(1).max(160)
20271
+ import { z as z76 } from "zod";
20272
+ var WinnerReferenceInput = z76.object({
20273
+ runId: z76.string().uuid(),
20274
+ candidateSourceId: z76.string().trim().min(1).max(160)
19249
20275
  }).strict().describe("Referencia opaca de un producto devuelto por find_winning_products/get_product_research_run.");
19250
20276
  async function resolveWinnerImages(api, winnerReference, images) {
19251
20277
  if (!winnerReference) return images;
@@ -19282,59 +20308,59 @@ var createProductTaskSingleOutputDescription = renderCreateProductTaskAvoidWhenF
19282
20308
  var createServiceVideoGuidance = getProductMakerCapabilityModelGuidance("create_service_video");
19283
20309
  var listTasksGuidance = getProductMakerCapabilityModelGuidance("list_tasks");
19284
20310
  var editTaskDraftGuidance = getProductMakerCapabilityModelGuidance("edit_task_draft");
19285
- var CreateProductTaskInput = z73.object({
20311
+ var CreateProductTaskInput = z77.object({
19286
20312
  images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
19287
20313
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
19288
- projectId: z73.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
19289
- productUrl: z73.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."),
20314
+ projectId: z77.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
20315
+ productUrl: z77.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."),
19290
20316
  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.'),
19291
20317
  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.'),
19292
- price: z73.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.'),
19293
- compareAtPrice: z73.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".'),
19294
- offer: z73.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.'),
19295
- paymentMethods: z73.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.'),
19296
- 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."),
19297
- productLabel: z73.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.'),
20318
+ price: z77.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.'),
20319
+ compareAtPrice: z77.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".'),
20320
+ offer: z77.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.'),
20321
+ paymentMethods: z77.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.'),
20322
+ 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."),
20323
+ productLabel: z77.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.'),
19298
20324
  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.'),
19299
- durationSeconds: z73.union([
19300
- z73.literal(15),
19301
- z73.literal(30)
20325
+ durationSeconds: z77.union([
20326
+ z77.literal(15),
20327
+ z77.literal(30)
19302
20328
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
19303
- pauseForAngleReview: z73.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."),
19304
- aiDecidesAll: z73.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."),
20329
+ pauseForAngleReview: z77.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."),
20330
+ aiDecidesAll: z77.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."),
19305
20331
  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.'),
19306
20332
  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.'),
19307
20333
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
19308
20334
  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).'),
19309
- videoAngleCount: z73.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."),
20335
+ videoAngleCount: z77.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."),
19310
20336
  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.'),
19311
- outputs: z73.array(z73.enum([
20337
+ outputs: z77.array(z77.enum([
19312
20338
  "landing",
19313
20339
  "video",
19314
20340
  "image"
19315
20341
  ])).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}`),
19316
- 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). 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."),
19317
- replicateWinnerWhenFound: z73.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."),
19318
- 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 los outputs generados."),
19319
- actorId: z73.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."),
20342
+ 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). 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."),
20343
+ replicateWinnerWhenFound: z77.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."),
20344
+ 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 los outputs generados."),
20345
+ actorId: z77.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."),
19320
20346
  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.'),
19321
- creativeDirection: z73.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.'),
19322
- headline: z73.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."),
19323
- subheadline: z73.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."),
20347
+ creativeDirection: z77.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.'),
20348
+ headline: z77.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."),
20349
+ subheadline: z77.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."),
19324
20350
  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".'),
19325
20351
  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".')
19326
20352
  }).strict();
19327
- var CreateServiceVideoInput = z73.object({
19328
- landingUrl: z73.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
19329
- additionalContext: z73.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
20353
+ var CreateServiceVideoInput = z77.object({
20354
+ landingUrl: z77.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
20355
+ additionalContext: z77.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
19330
20356
  language: Lang.describe("Idioma del video: es, en o pt."),
19331
- voiceId: z73.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.'),
19332
- brandId: z73.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
19333
- reviewMode: z73.enum([
20357
+ voiceId: z77.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.'),
20358
+ brandId: z77.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
20359
+ reviewMode: z77.enum([
19334
20360
  "automatic",
19335
20361
  "guided"
19336
20362
  ]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
19337
- videoPreset: z73.enum([
20363
+ videoPreset: z77.enum([
19338
20364
  "auto",
19339
20365
  "ugc_dynamic",
19340
20366
  "ugc_how_to",
@@ -19344,128 +20370,128 @@ var CreateServiceVideoInput = z73.object({
19344
20370
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
19345
20371
  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.')
19346
20372
  }).strict();
19347
- var GetTaskStatusInput = z73.object({
19348
- taskId: z73.string().min(1),
19349
- waitSeconds: z73.number().int().min(0).max(120).optional()
20373
+ var GetTaskStatusInput = z77.object({
20374
+ taskId: z77.string().min(1),
20375
+ waitSeconds: z77.number().int().min(0).max(120).optional()
19350
20376
  });
19351
- var ListTasksInput = z73.object({
19352
- limit: z73.number().int().min(1).max(50).optional(),
19353
- offset: z73.number().int().min(0).optional(),
19354
- search: z73.string().optional()
20377
+ var ListTasksInput = z77.object({
20378
+ limit: z77.number().int().min(1).max(50).optional(),
20379
+ offset: z77.number().int().min(0).optional(),
20380
+ search: z77.string().optional()
19355
20381
  });
19356
20382
  var MCP_REVIEW_LIST_FIELD_MAX = 1500;
19357
20383
  var MCP_REVIEW_FEATURE_ITEM_MAX = 240;
19358
- var EditTaskDraftInput = z73.object({
19359
- taskId: z73.string().min(1),
19360
- angleIndex: z73.number().int().min(0),
19361
- angle: z73.object({
19362
- angle: z73.string().min(1).max(480),
19363
- buyerPersona: z73.string().min(1).max(360),
19364
- actor: z73.string().min(1).max(360).optional(),
19365
- actorId: z73.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."),
19366
- gender: z73.enum([
20384
+ var EditTaskDraftInput = z77.object({
20385
+ taskId: z77.string().min(1),
20386
+ angleIndex: z77.number().int().min(0),
20387
+ angle: z77.object({
20388
+ angle: z77.string().min(1).max(480),
20389
+ buyerPersona: z77.string().min(1).max(360),
20390
+ actor: z77.string().min(1).max(360).optional(),
20391
+ actorId: z77.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."),
20392
+ gender: z77.enum([
19367
20393
  "female",
19368
20394
  "male"
19369
20395
  ]).optional(),
19370
- scriptNotes: z73.string().max(2e3).optional()
20396
+ scriptNotes: z77.string().max(2e3).optional()
19371
20397
  })
19372
20398
  });
19373
- var PendingActionPayload = z73.discriminatedUnion("kind", [
19374
- z73.object({
19375
- kind: z73.literal("product_reference_set_approval"),
20399
+ var PendingActionPayload = z77.discriminatedUnion("kind", [
20400
+ z77.object({
20401
+ kind: z77.literal("product_reference_set_approval"),
19376
20402
  scope: ProductReferenceScopeEnum.optional(),
19377
- scopeKey: z73.string().min(1).max(120).optional(),
19378
- scopeSnapshotId: z73.string().uuid().optional(),
19379
- expectedScopeHash: z73.string().min(1).max(128).optional(),
20403
+ scopeKey: z77.string().min(1).max(120).optional(),
20404
+ scopeSnapshotId: z77.string().uuid().optional(),
20405
+ expectedScopeHash: z77.string().min(1).max(128).optional(),
19380
20406
  decision: ProductReferenceResolutionDecisionEnum,
19381
20407
  reason: ProductReferenceRejectionReasonEnum.optional(),
19382
- affectedRefKeys: z73.array(z73.string().min(1).max(200)).min(1).optional(),
19383
- affectedPhotoNumbers: z73.array(z73.number().int().positive()).min(1).optional(),
19384
- recoveryAction: z73.enum([
20408
+ affectedRefKeys: z77.array(z77.string().min(1).max(200)).min(1).optional(),
20409
+ affectedPhotoNumbers: z77.array(z77.number().int().positive()).min(1).optional(),
20410
+ recoveryAction: z77.enum([
19385
20411
  "remove",
19386
20412
  "replace",
19387
20413
  "upload",
19388
20414
  "return_to_selection"
19389
20415
  ]).optional()
19390
20416
  }),
19391
- z73.object({
19392
- kind: z73.literal("product_reference_confirmation"),
19393
- selectedPhotoNumbers: z73.array(z73.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."),
20417
+ z77.object({
20418
+ kind: z77.literal("product_reference_confirmation"),
20419
+ selectedPhotoNumbers: z77.array(z77.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."),
19394
20420
  userChoice: ProductReferenceUserChoiceEnum.optional(),
19395
- confirmedProductNames: z73.array(z73.string().min(1).max(120)).min(2).max(8).optional(),
20421
+ confirmedProductNames: z77.array(z77.string().min(1).max(120)).min(2).max(8).optional(),
19396
20422
  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."),
19397
20423
  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."),
19398
- useMultiSceneShowcase: z73.boolean().optional().describe("Compatibilidad con confirmaciones antiguas: si es true junto con userChoice=variants, conserva el showcase multiescena.")
20424
+ useMultiSceneShowcase: z77.boolean().optional().describe("Compatibilidad con confirmaciones antiguas: si es true junto con userChoice=variants, conserva el showcase multiescena.")
19399
20425
  }),
19400
- z73.object({
19401
- kind: z73.literal("angle_review_approval"),
20426
+ z77.object({
20427
+ kind: z77.literal("angle_review_approval"),
19402
20428
  // All edits are optional — empty action body just approves the
19403
20429
  // backend-generated draft as-is. Field shapes mirror the angle-review
19404
20430
  // request contracts in @packages/types/wizard.ts; the handler routes to
19405
20431
  // the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
19406
- customAngle: z73.string().min(1).max(480).optional(),
20432
+ customAngle: z77.string().min(1).max(480).optional(),
19407
20433
  // Keep the shared MCP action within the strictest downstream contract:
19408
20434
  // autopilot/image resume endpoints cap these fields at 1500 characters.
19409
- painPoints: z73.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
19410
- benefits: z73.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
20435
+ painPoints: z77.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
20436
+ benefits: z77.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
19411
20437
  // Same policy as the two fields above: the strictest downstream contract
19412
20438
  // wins. The image endpoint caps each feature at 240 while landing/video
19413
20439
  // accept 300, and this single action routes to whichever the task is — so
19414
20440
  // accepting 300 here turns a 250-character feature on an image task into
19415
20441
  // an opaque HTTP 400 after the model already believed it had been saved.
19416
- features: z73.array(z73.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
19417
- buyerPersona: z73.string().min(1).max(360).optional(),
20442
+ features: z77.array(z77.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
20443
+ buyerPersona: z77.string().min(1).max(360).optional(),
19418
20444
  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.'),
19419
- actor: z73.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."),
19420
- actorId: z73.string().max(120).nullable().optional(),
19421
- creativeDirection: z73.string().max(2e3).optional(),
19422
- selectedAngleNumbers: z73.array(z73.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."),
19423
- angleEdits: z73.array(z73.object({
19424
- angleNumber: z73.number().int().positive(),
19425
- narrativeSelection: z73.enum([
20445
+ actor: z77.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."),
20446
+ actorId: z77.string().max(120).nullable().optional(),
20447
+ creativeDirection: z77.string().max(2e3).optional(),
20448
+ selectedAngleNumbers: z77.array(z77.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."),
20449
+ angleEdits: z77.array(z77.object({
20450
+ angleNumber: z77.number().int().positive(),
20451
+ narrativeSelection: z77.enum([
19426
20452
  "market",
19427
20453
  "guided"
19428
20454
  ]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo."),
19429
- angle: z73.string().min(1).max(480).optional(),
19430
- buyerPersona: z73.string().min(1).max(360).optional(),
20455
+ angle: z77.string().min(1).max(480).optional(),
20456
+ buyerPersona: z77.string().min(1).max(360).optional(),
19431
20457
  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.'),
19432
- actor: z73.string().min(1).max(360).optional(),
19433
- actorId: z73.string().max(120).nullable().optional(),
19434
- creativeDirection: z73.string().max(2e3).optional()
20458
+ actor: z77.string().min(1).max(360).optional(),
20459
+ actorId: z77.string().max(120).nullable().optional(),
20460
+ creativeDirection: z77.string().max(2e3).optional()
19435
20461
  }).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)."),
19436
- gapDurationSeconds: z73.union([
19437
- z73.literal(15),
19438
- z73.literal(30)
20462
+ gapDurationSeconds: z77.union([
20463
+ z77.literal(15),
20464
+ z77.literal(30)
19439
20465
  ]).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.")
19440
20466
  }),
19441
- z73.object({
19442
- kind: z73.literal("service_angle_approval"),
19443
- angleId: z73.string().uuid().optional(),
19444
- angleNumber: z73.number().int().positive().optional(),
19445
- content: z73.string().min(1).max(480).optional(),
19446
- creativeDirection: z73.string().min(1).max(1500).optional(),
19447
- actor: z73.string().min(1).max(360).optional()
20467
+ z77.object({
20468
+ kind: z77.literal("service_angle_approval"),
20469
+ angleId: z77.string().uuid().optional(),
20470
+ angleNumber: z77.number().int().positive().optional(),
20471
+ content: z77.string().min(1).max(480).optional(),
20472
+ creativeDirection: z77.string().min(1).max(1500).optional(),
20473
+ actor: z77.string().min(1).max(360).optional()
19448
20474
  }),
19449
- z73.object({
19450
- kind: z73.literal("service_script_approval"),
19451
- voiceovers: z73.record(z73.string(), z73.string().min(1).max(600)).optional(),
19452
- visualDirections: z73.record(z73.string(), z73.string().min(1).max(1200)).optional(),
19453
- voiceoversByScene: z73.record(z73.string(), z73.string().min(1).max(600)).optional(),
19454
- visualDirectionsByScene: z73.record(z73.string(), z73.string().min(1).max(1200)).optional()
20475
+ z77.object({
20476
+ kind: z77.literal("service_script_approval"),
20477
+ voiceovers: z77.record(z77.string(), z77.string().min(1).max(600)).optional(),
20478
+ visualDirections: z77.record(z77.string(), z77.string().min(1).max(1200)).optional(),
20479
+ voiceoversByScene: z77.record(z77.string(), z77.string().min(1).max(600)).optional(),
20480
+ visualDirectionsByScene: z77.record(z77.string(), z77.string().min(1).max(1200)).optional()
19455
20481
  })
19456
20482
  ]);
19457
- var ResolvePendingActionInput = z73.object({
19458
- taskId: z73.string().min(1),
20483
+ var ResolvePendingActionInput = z77.object({
20484
+ taskId: z77.string().min(1),
19459
20485
  action: PendingActionPayload
19460
20486
  }).strict();
19461
20487
  function resolveVariantConfirmVideoTier(action) {
19462
20488
  return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
19463
20489
  }
19464
20490
  __name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
19465
- var StatusForResolveSchema = z73.object({
20491
+ var StatusForResolveSchema = z77.object({
19466
20492
  // Metadata used to select the backend resume contract. Older API responses
19467
20493
  // may omit these fields, so the resolver keeps the generic fallback.
19468
- kind: z73.enum([
20494
+ kind: z77.enum([
19469
20495
  "autopilot",
19470
20496
  "video",
19471
20497
  "image",
@@ -19473,18 +20499,18 @@ var StatusForResolveSchema = z73.object({
19473
20499
  "mix",
19474
20500
  "service"
19475
20501
  ]).optional(),
19476
- selectedOutputs: z73.array(z73.enum([
20502
+ selectedOutputs: z77.array(z77.enum([
19477
20503
  "landing",
19478
20504
  "video",
19479
20505
  "image"
19480
20506
  ])).optional(),
19481
- videoAngleCount: z73.number().int().min(1).max(3).optional(),
19482
- ugcFormat: z73.enum([
20507
+ videoAngleCount: z77.number().int().min(1).max(3).optional(),
20508
+ ugcFormat: z77.enum([
19483
20509
  "single_8s",
19484
20510
  "single_15s",
19485
20511
  "double_30s"
19486
20512
  ]).optional(),
19487
- videoStyle: z73.enum([
20513
+ videoStyle: z77.enum([
19488
20514
  "ugc",
19489
20515
  "cartoon_3d",
19490
20516
  "product_only",
@@ -19493,79 +20519,72 @@ var StatusForResolveSchema = z73.object({
19493
20519
  videoStructure: VideoStructureEnum.optional(),
19494
20520
  videoQuality: VideoQualityEnum.optional(),
19495
20521
  videoRouteId: VideoRouteIdEnum.optional(),
19496
- videoModel: z73.enum([
19497
- "veo",
19498
- "wan",
19499
- "bytedance",
19500
- "seedance",
19501
- "grok",
19502
- "kling"
19503
- ]).optional(),
19504
- videoTier: z73.enum([
20522
+ videoModel: TaskVideoModelEnum.optional(),
20523
+ videoTier: z77.enum([
19505
20524
  "legacy",
19506
20525
  "standard",
19507
20526
  "pro",
19508
20527
  "single_call"
19509
20528
  ]).optional(),
19510
- videoResolution: z73.enum([
20529
+ videoResolution: z77.enum([
19511
20530
  "480p",
19512
20531
  "720p"
19513
20532
  ]).optional(),
19514
20533
  narrativeStyle: NarrativeStyleEnum.optional(),
19515
- pauseForVideoScriptReview: z73.boolean().optional(),
19516
- pendingUserAction: z73.object({
19517
- kind: z73.string(),
19518
- candidateGrouping: z73.string().optional(),
19519
- selectedImageUrls: z73.array(z73.string()).optional(),
19520
- proposedProductNames: z73.array(z73.string()).optional(),
20534
+ pauseForVideoScriptReview: z77.boolean().optional(),
20535
+ pendingUserAction: z77.object({
20536
+ kind: z77.string(),
20537
+ candidateGrouping: z77.string().optional(),
20538
+ selectedImageUrls: z77.array(z77.string()).optional(),
20539
+ proposedProductNames: z77.array(z77.string()).optional(),
19521
20540
  // Adaptive reference questions are server-owned. Echo only the evidence
19522
20541
  // identity and the choices shown by the current snapshot; never accept a
19523
20542
  // client-supplied reference set as authorization.
19524
- options: z73.array(ProductReferenceUserChoiceEnum).max(4).optional(),
19525
- mode: z73.enum([
20543
+ options: z77.array(ProductReferenceUserChoiceEnum).max(4).optional(),
20544
+ mode: z77.enum([
19526
20545
  "question",
19527
20546
  "visual_recovery"
19528
20547
  ]).optional(),
19529
20548
  /** Hand-mirrored from packages/types result.ts — see CLAUDE.md § MCP Server Sync. */
19530
- canKeepSelection: z73.boolean().optional(),
19531
- decisionKey: z73.string().optional(),
19532
- questionOrdinal: z73.number().int().min(1).max(5).optional(),
19533
- questionCeiling: z73.union([
19534
- z73.literal(3),
19535
- z73.literal(5)
20549
+ canKeepSelection: z77.boolean().optional(),
20550
+ decisionKey: z77.string().optional(),
20551
+ questionOrdinal: z77.number().int().min(1).max(5).optional(),
20552
+ questionCeiling: z77.union([
20553
+ z77.literal(3),
20554
+ z77.literal(5)
19536
20555
  ]).optional(),
19537
- referenceEvidenceVersion: z73.number().int().positive().optional(),
19538
- expectedEvidenceHash: z73.string().optional(),
19539
- question: z73.object({
19540
- prompt: z73.string().min(1).max(240),
19541
- helpText: z73.string().min(1).max(300).optional(),
19542
- options: z73.array(z73.object({
20556
+ referenceEvidenceVersion: z77.number().int().positive().optional(),
20557
+ expectedEvidenceHash: z77.string().optional(),
20558
+ question: z77.object({
20559
+ prompt: z77.string().min(1).max(240),
20560
+ helpText: z77.string().min(1).max(300).optional(),
20561
+ options: z77.array(z77.object({
19543
20562
  value: ProductReferenceUserChoiceEnum,
19544
- label: z73.string().min(1).max(120),
19545
- description: z73.string().min(1).max(240).optional()
20563
+ label: z77.string().min(1).max(120),
20564
+ description: z77.string().min(1).max(240).optional()
19546
20565
  })).min(2).max(4)
19547
20566
  }).optional(),
19548
20567
  scope: ProductReferenceScopeEnum.optional(),
19549
- scopeKey: z73.string().optional(),
19550
- scopeSnapshotId: z73.string().uuid().optional(),
19551
- scopeHash: z73.string().optional(),
19552
- baseReferenceSetHash: z73.string().optional(),
19553
- phase: z73.enum([
20568
+ scopeKey: z77.string().optional(),
20569
+ scopeSnapshotId: z77.string().uuid().optional(),
20570
+ scopeHash: z77.string().optional(),
20571
+ baseReferenceSetHash: z77.string().optional(),
20572
+ phase: z77.enum([
19554
20573
  "preparation",
19555
20574
  "final"
19556
20575
  ]).optional(),
19557
- selectedRefKeys: z73.array(z73.string().min(1).max(200)).optional(),
19558
- allowedDecisions: z73.array(ProductReferenceResolutionDecisionEnum).optional(),
19559
- previews: z73.array(z73.object({
19560
- refKey: z73.string().min(1).max(200)
20576
+ selectedRefKeys: z77.array(z77.string().min(1).max(200)).optional(),
20577
+ allowedDecisions: z77.array(ProductReferenceResolutionDecisionEnum).optional(),
20578
+ previews: z77.array(z77.object({
20579
+ refKey: z77.string().min(1).max(200)
19561
20580
  }).passthrough()).optional(),
19562
- customAngleDraft: z73.string().optional(),
19563
- painPointsDraft: z73.string().optional(),
19564
- benefitsDraft: z73.string().optional(),
19565
- featuresDraft: z73.array(z73.string()).optional(),
19566
- angleOptions: z73.array(z73.object({
19567
- index: z73.number().int().nonnegative(),
19568
- angle: z73.string().min(1)
20581
+ customAngleDraft: z77.string().optional(),
20582
+ painPointsDraft: z77.string().optional(),
20583
+ benefitsDraft: z77.string().optional(),
20584
+ featuresDraft: z77.array(z77.string()).optional(),
20585
+ angleOptions: z77.array(z77.object({
20586
+ index: z77.number().int().nonnegative(),
20587
+ angle: z77.string().min(1)
19569
20588
  }).passthrough()).min(1).max(5).optional(),
19570
20589
  // Recommendation consumed only when the caller explicitly sends
19571
20590
  // gapDurationSeconds: 30 in the angle approval action.
@@ -19575,17 +20594,17 @@ var StatusForResolveSchema = z73.object({
19575
20594
  // keys. `actorId` (the saved custom actor the picker stored) was silently
19576
20595
  // dropped here, so the approval handler below could not echo it back on the
19577
20596
  // PATCH — the selection looked accepted and reached no render.
19578
- angles: z73.array(z73.object({
19579
- id: z73.string().uuid(),
19580
- content: z73.string(),
19581
- creativeDirection: z73.string(),
19582
- actor: z73.string(),
19583
- actorId: z73.string().nullable().optional()
20597
+ angles: z77.array(z77.object({
20598
+ id: z77.string().uuid(),
20599
+ content: z77.string(),
20600
+ creativeDirection: z77.string(),
20601
+ actor: z77.string(),
20602
+ actorId: z77.string().nullable().optional()
19584
20603
  }).passthrough()).optional(),
19585
- panels: z73.array(z73.object({
19586
- beatId: z73.string(),
19587
- voiceover: z73.string(),
19588
- visualDirection: z73.string()
20604
+ panels: z77.array(z77.object({
20605
+ beatId: z77.string(),
20606
+ voiceover: z77.string(),
20607
+ visualDirection: z77.string()
19589
20608
  }).passthrough()).optional()
19590
20609
  }).optional()
19591
20610
  }).passthrough();
@@ -19784,10 +20803,25 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
19784
20803
  };
19785
20804
  const r = await api.postMultipart("/v1/ingest", {
19786
20805
  images: parts
19787
- }, fields);
20806
+ }, fields, {
20807
+ // LLM hosts retry paid tools with rephrased arguments — the API's
20808
+ // byte-fingerprint can't see the duplicate, this key can. Derived
20809
+ // from the semantic inputs, not the wire fields, for the same
20810
+ // reason (see request-key.ts, which also canonicalizes the
20811
+ // order-insignificant `outputs` set).
20812
+ idempotencyKey: derivePaidRequestKey({
20813
+ fingerprint: api.fingerprint(),
20814
+ tool: "create_product_task",
20815
+ input,
20816
+ nowMs: Date.now()
20817
+ })
20818
+ });
19788
20819
  return asText({
19789
20820
  taskId: r.taskId,
19790
- status: "queued",
20821
+ status: r.status ?? "queued",
20822
+ ...r.replayed ? {
20823
+ replayed: true
20824
+ } : {},
19791
20825
  estimatedSeconds: 300,
19792
20826
  outputs: selectedOutputs,
19793
20827
  outputMode: "single_task",
@@ -19824,26 +20858,56 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
19824
20858
  assets: []
19825
20859
  },
19826
20860
  appDemoVideos: []
20861
+ }, {
20862
+ // The 2026-09-25 incident: four retries of this exact tool created
20863
+ // four charged service tasks because the host rephrased
20864
+ // `additionalContext` and flipped the landing between `/` and
20865
+ // `/es`. The derived key is stable across both rewrites (see
20866
+ // request-key.ts).
20867
+ idempotencyKey: derivePaidRequestKey({
20868
+ fingerprint: api.fingerprint(),
20869
+ tool: "create_service_video",
20870
+ input,
20871
+ nowMs: Date.now()
20872
+ })
19827
20873
  });
19828
20874
  const angles = await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles`, {});
19829
20875
  const first = angles[0];
20876
+ const replayed = created.replayed === true;
19830
20877
  if (input.reviewMode === "automatic") {
19831
20878
  if (!first) throw new Error("ProductMaker no pudo crear un concepto para este servicio.");
19832
- await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles/select`, {
19833
- angleIds: [
19834
- first.id
19835
- ]
19836
- });
20879
+ if (!(replayed && created.status !== "queued")) {
20880
+ await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles/select`, {
20881
+ angleIds: [
20882
+ first.id
20883
+ ]
20884
+ });
20885
+ }
19837
20886
  return asText({
19838
20887
  taskId: created.taskId,
19839
- status: "processing",
20888
+ status: replayed ? created.status ?? "processing" : "processing",
20889
+ ...replayed ? {
20890
+ replayed: true
20891
+ } : {},
19840
20892
  reviewMode: input.reviewMode,
19841
20893
  selectedConcept: first
19842
20894
  });
19843
20895
  }
20896
+ if (replayed && created.status !== "queued") {
20897
+ return asText({
20898
+ taskId: created.taskId,
20899
+ status: created.status,
20900
+ replayed: true,
20901
+ reviewMode: input.reviewMode,
20902
+ angles
20903
+ });
20904
+ }
19844
20905
  return asText({
19845
20906
  taskId: created.taskId,
19846
20907
  status: "waiting_for_user",
20908
+ ...replayed ? {
20909
+ replayed: true
20910
+ } : {},
19847
20911
  reviewMode: input.reviewMode,
19848
20912
  pendingUserAction: {
19849
20913
  kind: "service_angle_approval",
@@ -20419,8 +21483,7 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
20419
21483
  __name(registerTaskTools, "registerTaskTools");
20420
21484
 
20421
21485
  // src/tools/creatives.ts
20422
- import { randomUUID } from "crypto";
20423
- import { z as z74 } from "zod";
21486
+ import { z as z78 } from "zod";
20424
21487
  var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
20425
21488
  var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
20426
21489
  var generateImageCreativesGuidance = getProductMakerCapabilityModelGuidance("generate_image_creatives");
@@ -20430,70 +21493,70 @@ var CreativeRefs = {
20430
21493
  images: ImagesInput,
20431
21494
  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.'),
20432
21495
  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.'),
20433
- price: z74.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.'),
20434
- offer: z74.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.'),
20435
- compareAtPrice: z74.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
20436
- productContext: z74.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."),
20437
- productLabel: z74.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.'),
20438
- buyerPersona: z74.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.'),
20439
- isCombo: z74.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
20440
- competitorScanEnabled: z74.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."),
20441
- brandId: z74.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.")
21496
+ price: z78.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.'),
21497
+ offer: z78.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.'),
21498
+ compareAtPrice: z78.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
21499
+ productContext: z78.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."),
21500
+ productLabel: z78.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.'),
21501
+ buyerPersona: z78.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.'),
21502
+ isCombo: z78.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
21503
+ competitorScanEnabled: z78.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."),
21504
+ brandId: z78.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.")
20442
21505
  };
20443
- var VideoPresenter = z74.discriminatedUnion("mode", [
20444
- z74.object({
20445
- mode: z74.literal("none")
21506
+ var VideoPresenter = z78.discriminatedUnion("mode", [
21507
+ z78.object({
21508
+ mode: z78.literal("none")
20446
21509
  }).strict(),
20447
- z74.object({
20448
- mode: z74.literal("saved_actor"),
20449
- actorId: z74.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
21510
+ z78.object({
21511
+ mode: z78.literal("saved_actor"),
21512
+ actorId: z78.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
20450
21513
  }).strict(),
20451
- z74.object({
20452
- mode: z74.literal("described"),
20453
- description: z74.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
21514
+ z78.object({
21515
+ mode: z78.literal("described"),
21516
+ description: z78.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
20454
21517
  }).strict()
20455
21518
  ]);
20456
- var VideoCreativeSetup = z74.discriminatedUnion("mode", [
20457
- z74.object({
20458
- mode: z74.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
21519
+ var VideoCreativeSetup = z78.discriminatedUnion("mode", [
21520
+ z78.object({
21521
+ mode: z78.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
20459
21522
  }).strict(),
20460
- z74.object({
20461
- mode: z74.literal("guided"),
21523
+ z78.object({
21524
+ mode: z78.literal("guided"),
20462
21525
  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.'),
20463
- buyerPersona: z74.string().trim().min(1).max(360),
21526
+ buyerPersona: z78.string().trim().min(1).max(360),
20464
21527
  presenter: VideoPresenter,
20465
- creativeDirection: z74.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
21528
+ creativeDirection: z78.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
20466
21529
  }).strict()
20467
21530
  ]);
20468
- var VideoInput = z74.object({
21531
+ var VideoInput = z78.object({
20469
21532
  images: CreativeRefs.images.optional(),
20470
21533
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
20471
- title: z74.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
20472
- primaryAngle: z74.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".'),
21534
+ title: z78.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
21535
+ primaryAngle: z78.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".'),
20473
21536
  language: CreativeRefs.language,
20474
21537
  country: CreativeRefs.country,
20475
- aspectRatio: z74.enum([
21538
+ aspectRatio: z78.enum([
20476
21539
  "9:16",
20477
21540
  "16:9",
20478
21541
  "Auto"
20479
21542
  ]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
20480
- durationSeconds: z74.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."),
20481
- videoAngleCount: z74.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."),
21543
+ durationSeconds: z78.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."),
21544
+ videoAngleCount: z78.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."),
20482
21545
  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.'),
20483
21546
  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."),
20484
21547
  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.'),
20485
21548
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
20486
21549
  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.'),
20487
- features: z74.array(z74.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
21550
+ features: z78.array(z78.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
20488
21551
  price: CreativeRefs.price,
20489
21552
  offer: CreativeRefs.offer,
20490
21553
  productContext: CreativeRefs.productContext,
20491
21554
  productLabel: CreativeRefs.productLabel,
20492
- cta: z74.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.'),
20493
- scriptNotes: z74.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.'),
21555
+ cta: z78.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.'),
21556
+ scriptNotes: z78.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.'),
20494
21557
  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.'),
20495
21558
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
20496
- replicateWinnerWhenFound: z74.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."),
21559
+ replicateWinnerWhenFound: z78.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."),
20497
21560
  brandId: CreativeRefs.brandId
20498
21561
  }).strict();
20499
21562
  var FreeVideoQualityEnum = VideoQualityEnum;
@@ -20511,7 +21574,7 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
20511
21574
  length: 29
20512
21575
  }, (_, index) => index + 2)
20513
21576
  };
20514
- var FreeVideoModelIdEnum = z74.enum([
21577
+ var FreeVideoModelIdEnum = z78.enum([
20515
21578
  "grok-imagine-video-1-5-preview",
20516
21579
  "minimax-h3/reference-to-video",
20517
21580
  "wan/3-0-video",
@@ -20522,70 +21585,65 @@ var FREE_SELECTABLE_MODEL_IDS = [
20522
21585
  "minimax-h3/reference-to-video",
20523
21586
  "wan/3-0-video"
20524
21587
  ];
20525
- var FreeVideoInput = z74.object({
21588
+ var FreeVideoInput = z78.object({
20526
21589
  images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
20527
- rawPrompt: z74.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."),
21590
+ rawPrompt: z78.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."),
20528
21591
  language: CreativeRefs.language,
20529
21592
  quality: FreeVideoQualityEnum.describe('Calidad del video: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan).'),
20530
21593
  // Retired in MCP 7.0.0 (Task 11): kept in the schema, accepted, and
20531
21594
  // parsed ONLY so the handler can reject it with a message naming its
20532
21595
  // replacement — dropping it silently would turn a caller's explicit
20533
21596
  // Pro++ pick into a quiet fallback to Wan.
20534
- freeProModel: z74.enum([
21597
+ freeProModel: z78.enum([
20535
21598
  "wan",
20536
21599
  "seedance"
20537
21600
  ]).optional().describe("ELIMINADO en MCP 7.0.0 \u2014 usa `modelId`. Enviar este campo produce un error."),
20538
21601
  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.'),
20539
- durationSeconds: z74.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."),
21602
+ durationSeconds: z78.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."),
20540
21603
  videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
20541
- generateAiMusic: z74.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
20542
- addSubtitles: z74.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
20543
- idempotencyKey: z74.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.")
21604
+ generateAiMusic: z78.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
21605
+ addSubtitles: z78.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
21606
+ idempotencyKey: z78.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.")
20544
21607
  }).strict();
20545
21608
  var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
20546
- var ImageCreativeSetup = z74.discriminatedUnion("mode", [
20547
- z74.object({
20548
- mode: z74.literal("automatic")
21609
+ var ImageCreativeSetup = z78.discriminatedUnion("mode", [
21610
+ z78.object({
21611
+ mode: z78.literal("automatic")
20549
21612
  }).strict(),
20550
- z74.object({
20551
- mode: z74.literal("guided"),
20552
- creativeArchetype: z74.union([
21613
+ z78.object({
21614
+ mode: z78.literal("guided"),
21615
+ creativeArchetype: z78.union([
20553
21616
  ImageCreativeArchetypeEnum,
20554
- z74.literal("ai_decides")
21617
+ z78.literal("ai_decides")
20555
21618
  ]),
20556
- visualTreatment: z74.union([
21619
+ visualTreatment: z78.union([
20557
21620
  ImageCreativeVisualTreatmentEnum,
20558
- z74.literal("ai_decides")
21621
+ z78.literal("ai_decides")
20559
21622
  ]),
20560
21623
  aspectRatio: ImageCreativeAspectRatioEnum,
20561
- nVariants: z74.union([
20562
- z74.literal(1),
20563
- z74.literal(2),
20564
- z74.literal(3),
20565
- z74.literal(4),
20566
- z74.literal(6)
21624
+ nVariants: z78.union([
21625
+ z78.literal(1),
21626
+ z78.literal(2),
21627
+ z78.literal(3),
21628
+ z78.literal(4),
21629
+ z78.literal(6)
20567
21630
  ]),
20568
- imageModel: z74.enum([
20569
- "gpt-image-2-5:1k",
20570
- "gpt-image-2-5:2k",
20571
- "gpt-image-2:1k",
20572
- "gpt-image-2:2k"
20573
- ]),
20574
- styleMix: z74.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
20575
- generateAdCopy: z74.boolean(),
20576
- competitorScanEnabled: z74.boolean(),
20577
- productContext: z74.string().trim().min(1).max(3e3).nullable(),
20578
- creativeDirection: z74.string().trim().min(1).max(500).nullable(),
20579
- brandColors: z74.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.'),
20580
- headline: z74.string().trim().min(1).max(120).nullable(),
20581
- subheadline: z74.string().trim().min(1).max(160).nullable()
21631
+ imageModel: z78.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.'),
21632
+ styleMix: z78.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
21633
+ generateAdCopy: z78.boolean(),
21634
+ competitorScanEnabled: z78.boolean(),
21635
+ productContext: z78.string().trim().min(1).max(3e3).nullable(),
21636
+ creativeDirection: z78.string().trim().min(1).max(500).nullable(),
21637
+ brandColors: z78.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.'),
21638
+ headline: z78.string().trim().min(1).max(120).nullable(),
21639
+ subheadline: z78.string().trim().min(1).max(160).nullable()
20582
21640
  }).strict()
20583
21641
  ]).superRefine((setup, context) => {
20584
21642
  if (setup.mode !== "guided") return;
20585
21643
  const needsStyleMix = setup.nVariants >= 2 && setup.nVariants <= 4;
20586
21644
  if (needsStyleMix && setup.styleMix === null) {
20587
21645
  context.addIssue({
20588
- code: z74.ZodIssueCode.custom,
21646
+ code: z78.ZodIssueCode.custom,
20589
21647
  path: [
20590
21648
  "styleMix"
20591
21649
  ],
@@ -20594,16 +21652,16 @@ var ImageCreativeSetup = z74.discriminatedUnion("mode", [
20594
21652
  }
20595
21653
  if (!needsStyleMix && setup.styleMix !== null) {
20596
21654
  context.addIssue({
20597
- code: z74.ZodIssueCode.custom,
21655
+ code: z78.ZodIssueCode.custom,
20598
21656
  path: [
20599
21657
  "styleMix"
20600
21658
  ],
20601
21659
  message: "styleMix debe ser null cuando nVariants es 1 o 6."
20602
21660
  });
20603
21661
  }
20604
- if ((setup.imageModel === "gpt-image-2:2k" || setup.imageModel === "gpt-image-2-5:2k") && setup.aspectRatio === "4:5") {
21662
+ if (imageResolutionOf(setup.imageModel) === "2k" && setup.aspectRatio === "4:5") {
20605
21663
  context.addIssue({
20606
- code: z74.ZodIssueCode.custom,
21664
+ code: z78.ZodIssueCode.custom,
20607
21665
  path: [
20608
21666
  "aspectRatio"
20609
21667
  ],
@@ -20611,53 +21669,53 @@ var ImageCreativeSetup = z74.discriminatedUnion("mode", [
20611
21669
  });
20612
21670
  }
20613
21671
  });
20614
- var LandingCreativeSetup = z74.discriminatedUnion("mode", [
20615
- z74.object({
20616
- mode: z74.literal("automatic")
21672
+ var LandingCreativeSetup = z78.discriminatedUnion("mode", [
21673
+ z78.object({
21674
+ mode: z78.literal("automatic")
20617
21675
  }).strict(),
20618
- z74.object({
20619
- mode: z74.literal("guided"),
21676
+ z78.object({
21677
+ mode: z78.literal("guided"),
20620
21678
  landingStrategy: LandingStrategyEnum,
20621
21679
  landingImageFormat: LandingImageFormatEnum,
20622
- competitorScanEnabled: z74.boolean(),
20623
- paymentMethods: z74.string().trim().min(1).max(240).nullable(),
20624
- productContext: z74.string().trim().min(1).max(3e3).nullable(),
20625
- creativeDirection: z74.string().trim().min(1).max(500).nullable(),
20626
- headline: z74.string().trim().min(1).max(120).nullable(),
20627
- subheadline: z74.string().trim().min(1).max(160).nullable()
21680
+ competitorScanEnabled: z78.boolean(),
21681
+ paymentMethods: z78.string().trim().min(1).max(240).nullable(),
21682
+ productContext: z78.string().trim().min(1).max(3e3).nullable(),
21683
+ creativeDirection: z78.string().trim().min(1).max(500).nullable(),
21684
+ headline: z78.string().trim().min(1).max(120).nullable(),
21685
+ subheadline: z78.string().trim().min(1).max(160).nullable()
20628
21686
  }).strict()
20629
21687
  ]);
20630
- var ImageInput = z74.object({
21688
+ var ImageInput = z78.object({
20631
21689
  images: CreativeRefs.images.optional(),
20632
21690
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
20633
- productTitle: z74.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
20634
- primaryAngle: z74.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.'),
21691
+ productTitle: z78.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
21692
+ primaryAngle: z78.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.'),
20635
21693
  buyerPersona: CreativeRefs.buyerPersona,
20636
21694
  language: CreativeRefs.language,
20637
21695
  country: CreativeRefs.country,
20638
21696
  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."),
20639
21697
  imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
20640
- features: z74.array(z74.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
21698
+ features: z78.array(z78.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
20641
21699
  price: CreativeRefs.price,
20642
- omitPrice: z74.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."),
21700
+ omitPrice: z78.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."),
20643
21701
  compareAtPrice: CreativeRefs.compareAtPrice,
20644
21702
  offer: CreativeRefs.offer,
20645
21703
  productLabel: CreativeRefs.productLabel,
20646
21704
  isCombo: CreativeRefs.isCombo,
20647
21705
  brandId: CreativeRefs.brandId
20648
21706
  });
20649
- var EditImageCreativeInput = z74.object({
20650
- taskId: z74.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
20651
- variantIndex: z74.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
20652
- instruction: z74.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".')
21707
+ var EditImageCreativeInput = z78.object({
21708
+ taskId: z78.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
21709
+ variantIndex: z78.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
21710
+ instruction: z78.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".')
20653
21711
  });
20654
- var LandingInput = z74.object({
21712
+ var LandingInput = z78.object({
20655
21713
  images: CreativeRefs.images.optional(),
20656
21714
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
20657
21715
  language: CreativeRefs.language,
20658
21716
  country: CreativeRefs.country,
20659
21717
  creativeSetup: LandingCreativeSetup.describe("Configuraci\xF3n obligatoria de landing cl\xE1sica equivalente al wizard web. No expone landing editable/code mientras siga siendo admin-only."),
20660
- salesAngle: z74.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.'),
21718
+ salesAngle: z78.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.'),
20661
21719
  buyerPersona: CreativeRefs.buyerPersona,
20662
21720
  price: CreativeRefs.price,
20663
21721
  compareAtPrice: CreativeRefs.compareAtPrice,
@@ -20676,7 +21734,7 @@ function resolveImageSetup(setup) {
20676
21734
  },
20677
21735
  aspectRatio: setup.aspectRatio,
20678
21736
  nVariants: setup.nVariants,
20679
- imageModel: setup.imageModel,
21737
+ imageModel: canonicalImageModelInput(setup.imageModel),
20680
21738
  ...setup.nVariants === 6 ? {
20681
21739
  styleMix: true
20682
21740
  } : setup.styleMix === null ? {} : {
@@ -20767,7 +21825,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
20767
21825
  addSubtitles: input.addSubtitles
20768
21826
  },
20769
21827
  allowEmptyImages: true,
20770
- idempotencyKey: input.idempotencyKey ?? randomUUID(),
21828
+ // Always derived — never `input.idempotencyKey ??`: a host that
21829
+ // mints a fresh UUID per retry would defeat the dedupe entirely.
21830
+ // The semantic key is also a strictly better retry identity: a
21831
+ // reused caller key still lands on it (see request-key.ts).
21832
+ idempotencyKey: derivePaidRequestKey({
21833
+ fingerprint: api.fingerprint(),
21834
+ tool: "generate_free_video",
21835
+ input,
21836
+ nowMs: Date.now()
21837
+ }),
20771
21838
  transport
20772
21839
  });
20773
21840
  } catch (e) {
@@ -20832,6 +21899,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
20832
21899
  path: "/v1/creatives/videos",
20833
21900
  estimatedSeconds: 300,
20834
21901
  input: wirePayload,
21902
+ idempotencyKey: derivePaidRequestKey({
21903
+ fingerprint: api.fingerprint(),
21904
+ tool: "generate_video_creative",
21905
+ input,
21906
+ nowMs: Date.now()
21907
+ }),
20835
21908
  transport
20836
21909
  });
20837
21910
  } catch (e) {
@@ -20866,6 +21939,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
20866
21939
  pauseForAngleReview: true
20867
21940
  }
20868
21941
  },
21942
+ idempotencyKey: derivePaidRequestKey({
21943
+ fingerprint: api.fingerprint(),
21944
+ tool: "generate_image_creatives",
21945
+ input,
21946
+ nowMs: Date.now()
21947
+ }),
20869
21948
  transport
20870
21949
  });
20871
21950
  } catch (e) {
@@ -20909,6 +21988,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
20909
21988
  pauseForAngleReview: true
20910
21989
  }
20911
21990
  },
21991
+ idempotencyKey: derivePaidRequestKey({
21992
+ fingerprint: api.fingerprint(),
21993
+ tool: "generate_landing",
21994
+ input,
21995
+ nowMs: Date.now()
21996
+ }),
20912
21997
  transport
20913
21998
  });
20914
21999
  } catch (e) {
@@ -20919,10 +22004,10 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
20919
22004
  __name(registerCreativeTools, "registerCreativeTools");
20920
22005
 
20921
22006
  // src/tools/creative-variants.ts
20922
- import { z as z75 } from "zod";
22007
+ import { z as z79 } from "zod";
20923
22008
  var estimateCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("estimate_creative_variant");
20924
22009
  var createCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("create_creative_variant");
20925
- var LandingSectionKeyEnum = z75.enum([
22010
+ var LandingSectionKeyEnum = z79.enum([
20926
22011
  "problem_hook",
20927
22012
  "problem_cost",
20928
22013
  "solution_mechanism",
@@ -20962,24 +22047,24 @@ var LandingSectionKeyEnum = z75.enum([
20962
22047
  "lifestyle_fit",
20963
22048
  "join_lifestyle"
20964
22049
  ]);
20965
- var CreativeVariantKindEnum = z75.enum([
22050
+ var CreativeVariantKindEnum = z79.enum([
20966
22051
  "video",
20967
22052
  "image",
20968
22053
  "landing"
20969
22054
  ]);
20970
- var EstimateCreativeVariantInputBase = z75.object({
20971
- sourceTaskId: z75.string().uuid().describe("ID de la tarea original completada."),
22055
+ var EstimateCreativeVariantInputBase = z79.object({
22056
+ sourceTaskId: z79.string().uuid().describe("ID de la tarea original completada."),
20972
22057
  kind: CreativeVariantKindEnum,
20973
22058
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
20974
22059
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
20975
- nVariants: z75.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
22060
+ nVariants: z79.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
20976
22061
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
20977
- instructions: z75.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.")
22062
+ instructions: z79.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.")
20978
22063
  }).strict();
20979
22064
  var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
20980
22065
  if (input.kind === "video" && input.nVariants !== void 0) {
20981
22066
  ctx.addIssue({
20982
- code: z75.ZodIssueCode.custom,
22067
+ code: z79.ZodIssueCode.custom,
20983
22068
  path: [
20984
22069
  "nVariants"
20985
22070
  ],
@@ -20989,7 +22074,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
20989
22074
  if (input.kind !== "video") {
20990
22075
  if (input.videoPreset !== void 0) {
20991
22076
  ctx.addIssue({
20992
- code: z75.ZodIssueCode.custom,
22077
+ code: z79.ZodIssueCode.custom,
20993
22078
  path: [
20994
22079
  "videoPreset"
20995
22080
  ],
@@ -20998,7 +22083,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
20998
22083
  }
20999
22084
  if (input.videoResolution !== void 0) {
21000
22085
  ctx.addIssue({
21001
- code: z75.ZodIssueCode.custom,
22086
+ code: z79.ZodIssueCode.custom,
21002
22087
  path: [
21003
22088
  "videoResolution"
21004
22089
  ],
@@ -21008,7 +22093,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
21008
22093
  }
21009
22094
  if (input.kind !== "image" && input.nVariants !== void 0) {
21010
22095
  ctx.addIssue({
21011
- code: z75.ZodIssueCode.custom,
22096
+ code: z79.ZodIssueCode.custom,
21012
22097
  path: [
21013
22098
  "nVariants"
21014
22099
  ],
@@ -21018,7 +22103,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
21018
22103
  if (input.kind !== "landing") {
21019
22104
  if (input.sectionKey !== void 0) {
21020
22105
  ctx.addIssue({
21021
- code: z75.ZodIssueCode.custom,
22106
+ code: z79.ZodIssueCode.custom,
21022
22107
  path: [
21023
22108
  "sectionKey"
21024
22109
  ],
@@ -21027,7 +22112,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
21027
22112
  }
21028
22113
  if (input.instructions !== void 0) {
21029
22114
  ctx.addIssue({
21030
- code: z75.ZodIssueCode.custom,
22115
+ code: z79.ZodIssueCode.custom,
21031
22116
  path: [
21032
22117
  "instructions"
21033
22118
  ],
@@ -21036,31 +22121,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
21036
22121
  }
21037
22122
  }
21038
22123
  });
21039
- var CreateCreativeVariantInputBase = z75.object({
21040
- sourceTaskId: z75.string().uuid().describe("ID de la tarea original completada."),
22124
+ var CreateCreativeVariantInputBase = z79.object({
22125
+ sourceTaskId: z79.string().uuid().describe("ID de la tarea original completada."),
21041
22126
  kind: CreativeVariantKindEnum,
21042
- confirm: z75.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
21043
- clientRequestId: z75.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."),
21044
- angle: z75.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
21045
- buyerPersona: z75.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
21046
- scriptNotes: z75.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
21047
- creativeDirection: z75.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
22127
+ confirm: z79.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
22128
+ clientRequestId: z79.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."),
22129
+ angle: z79.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
22130
+ buyerPersona: z79.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
22131
+ scriptNotes: z79.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
22132
+ creativeDirection: z79.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
21048
22133
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
21049
22134
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
21050
- generateAiMusic: z75.boolean().optional().describe("Opcional para video."),
21051
- nVariants: z75.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
22135
+ generateAiMusic: z79.boolean().optional().describe("Opcional para video."),
22136
+ nVariants: z79.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
21052
22137
  imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
21053
22138
  creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
21054
22139
  visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
21055
22140
  aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
21056
- styleMix: z75.boolean().optional().describe("Opcional para imagen."),
22141
+ styleMix: z79.boolean().optional().describe("Opcional para imagen."),
21057
22142
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
21058
- instructions: z75.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.")
22143
+ instructions: z79.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.")
21059
22144
  }).strict();
21060
22145
  var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
21061
22146
  if ((input.kind === "video" || input.kind === "image") && !input.angle) {
21062
22147
  ctx.addIssue({
21063
- code: z75.ZodIssueCode.custom,
22148
+ code: z79.ZodIssueCode.custom,
21064
22149
  path: [
21065
22150
  "angle"
21066
22151
  ],
@@ -21069,7 +22154,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
21069
22154
  }
21070
22155
  if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
21071
22156
  ctx.addIssue({
21072
- code: z75.ZodIssueCode.custom,
22157
+ code: z79.ZodIssueCode.custom,
21073
22158
  path: [
21074
22159
  "instructions"
21075
22160
  ],
@@ -21115,7 +22200,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
21115
22200
  for (const field of fieldsByKind[input.kind]) {
21116
22201
  if (input[field] !== void 0) {
21117
22202
  ctx.addIssue({
21118
- code: z75.ZodIssueCode.custom,
22203
+ code: z79.ZodIssueCode.custom,
21119
22204
  path: [
21120
22205
  field
21121
22206
  ],
@@ -21162,7 +22247,7 @@ function registerCreativeVariantTools(server, api) {
21162
22247
  throw translateError(error);
21163
22248
  }
21164
22249
  });
21165
- 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) => {
22250
+ 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) => {
21166
22251
  try {
21167
22252
  const validatedInput = parseVariantInput(CreateCreativeVariantInput, input);
21168
22253
  const estimateInput = EstimateCreativeVariantInput.parse({
@@ -21189,8 +22274,16 @@ function registerCreativeVariantTools(server, api) {
21189
22274
  });
21190
22275
  sectionKey = LandingSectionKeyEnum.parse(recommendation.value);
21191
22276
  }
22277
+ const derivedClientRequestId = derivePaidRequestKey({
22278
+ fingerprint: api.fingerprint(),
22279
+ tool: "create_creative_variant",
22280
+ input: {
22281
+ ...validatedInput
22282
+ },
22283
+ nowMs: Date.now()
22284
+ });
21192
22285
  const payload = {
21193
- clientRequestId: validatedInput.clientRequestId,
22286
+ clientRequestId: derivedClientRequestId,
21194
22287
  kind: validatedInput.kind,
21195
22288
  ...validatedInput.kind !== "landing" ? {
21196
22289
  angle: validatedInput.angle,
@@ -21216,7 +22309,7 @@ function registerCreativeVariantTools(server, api) {
21216
22309
  };
21217
22310
  const created = await api.postJson(`/v1/tasks/${encodeURIComponent(validatedInput.sourceTaskId)}/creative-variants`, payload);
21218
22311
  if (created.status === "error") {
21219
- throw mcp(McpErrorCode.CREATIVE_VARIANT_FAILED, "La variante anterior termin\xF3 con error. Pide confirmaci\xF3n para un intento nuevo y usa otro clientRequestId.", {
22312
+ 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.", {
21220
22313
  variantTaskId: created.variantTaskId
21221
22314
  });
21222
22315
  }
@@ -21232,32 +22325,32 @@ function registerCreativeVariantTools(server, api) {
21232
22325
  __name(registerCreativeVariantTools, "registerCreativeVariantTools");
21233
22326
 
21234
22327
  // src/tools/script-review.ts
21235
- import { z as z76 } from "zod";
22328
+ import { z as z80 } from "zod";
21236
22329
  var getVideoScriptGuidance = getProductMakerCapabilityModelGuidance("get_video_script");
21237
22330
  var regenerateVideoScriptGuidance = getProductMakerCapabilityModelGuidance("regenerate_video_script");
21238
22331
  var approveVideoScriptGuidance = getProductMakerCapabilityModelGuidance("approve_video_script");
21239
- var GetVideoScriptInput = z76.object({
21240
- taskId: z76.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
21241
- angleKey: z76.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.')
22332
+ var GetVideoScriptInput = z80.object({
22333
+ taskId: z80.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
22334
+ angleKey: z80.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.')
21242
22335
  }).strict();
21243
22336
  var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
21244
22337
  var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
21245
- var ApproveSceneEdit = z76.object({
21246
- index: z76.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
21247
- dialogue: z76.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
22338
+ var ApproveSceneEdit = z80.object({
22339
+ index: z80.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
22340
+ dialogue: z80.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
21248
22341
  message: "dialogue no puede ser s\xF3lo espacios"
21249
22342
  }).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.")
21250
22343
  }).strict();
21251
- var ApproveVideoScriptInput = z76.object({
21252
- taskId: z76.string().min(1).describe("ID de la tarea."),
21253
- angleKey: z76.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.'),
21254
- scenes: z76.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.")
22344
+ var ApproveVideoScriptInput = z80.object({
22345
+ taskId: z80.string().min(1).describe("ID de la tarea."),
22346
+ angleKey: z80.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.'),
22347
+ scenes: z80.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.")
21255
22348
  }).strict();
21256
- var RegenerateVideoScriptInput = z76.object({
21257
- taskId: z76.string().min(1).describe("ID de la tarea."),
21258
- angleKey: z76.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.'),
22349
+ var RegenerateVideoScriptInput = z80.object({
22350
+ taskId: z80.string().min(1).describe("ID de la tarea."),
22351
+ angleKey: z80.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.'),
21259
22352
  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).'),
21260
- narrativeSelection: z76.enum([
22353
+ narrativeSelection: z80.enum([
21261
22354
  "market",
21262
22355
  "guided"
21263
22356
  ]).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.")
@@ -21338,14 +22431,14 @@ function registerScriptReviewTools(server, api) {
21338
22431
  __name(registerScriptReviewTools, "registerScriptReviewTools");
21339
22432
 
21340
22433
  // src/tools/connections.ts
21341
- import { z as z77 } from "zod";
22434
+ import { z as z81 } from "zod";
21342
22435
  var listShopifyShopsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_shops");
21343
22436
  var listShopifyProductsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_products");
21344
22437
  var listMetaAdAccountsGuidance = getProductMakerCapabilityModelGuidance("list_meta_ad_accounts");
21345
22438
  var listMetaDestinationsGuidance = getProductMakerCapabilityModelGuidance("list_meta_destinations");
21346
22439
  var listTikTokAdvertisersGuidance = getProductMakerCapabilityModelGuidance("list_tiktok_advertisers");
21347
22440
  function registerConnectionTools(server, api) {
21348
- 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.", z77.object({}).shape, READS_INTERNAL, async () => {
22441
+ 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.", z81.object({}).shape, READS_INTERNAL, async () => {
21349
22442
  try {
21350
22443
  const r = await api.getJson("/v1/shopify/shops");
21351
22444
  return asText(r);
@@ -21353,10 +22446,10 @@ function registerConnectionTools(server, api) {
21353
22446
  throw translateError(e);
21354
22447
  }
21355
22448
  });
21356
- 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.', z77.object({
21357
- shopDomain: z77.string().min(3).max(120),
21358
- query: z77.string().trim().max(120).optional(),
21359
- taskId: z77.string().uuid().optional()
22449
+ 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.', z81.object({
22450
+ shopDomain: z81.string().min(3).max(120),
22451
+ query: z81.string().trim().max(120).optional(),
22452
+ taskId: z81.string().uuid().optional()
21360
22453
  }).shape, READS_EXTERNAL, async ({ shopDomain, query, taskId }) => {
21361
22454
  try {
21362
22455
  const params = new URLSearchParams({
@@ -21370,7 +22463,7 @@ function registerConnectionTools(server, api) {
21370
22463
  throw translateError(e);
21371
22464
  }
21372
22465
  });
21373
- 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.", z77.object({}).shape, READS_EXTERNAL, async () => {
22466
+ 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.", z81.object({}).shape, READS_EXTERNAL, async () => {
21374
22467
  try {
21375
22468
  const r = await api.getJson("/v1/facebook/ad-accounts");
21376
22469
  return asText(r);
@@ -21380,7 +22473,7 @@ function registerConnectionTools(server, api) {
21380
22473
  });
21381
22474
  server.registerTool("list_meta_destinations", {
21382
22475
  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.",
21383
- inputSchema: z77.object({}).shape,
22476
+ inputSchema: z81.object({}).shape,
21384
22477
  annotations: READS_EXTERNAL,
21385
22478
  _meta: {
21386
22479
  "openai/widgetAccessible": true
@@ -21393,7 +22486,7 @@ function registerConnectionTools(server, api) {
21393
22486
  throw translateError(e);
21394
22487
  }
21395
22488
  });
21396
- server.tool("list_tiktok_advertisers", listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z77.object({}).shape, READS_EXTERNAL, async () => {
22489
+ server.tool("list_tiktok_advertisers", listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z81.object({}).shape, READS_EXTERNAL, async () => {
21397
22490
  try {
21398
22491
  const r = await api.getJson("/v1/tiktok/advertisers");
21399
22492
  return asText(r);
@@ -21405,10 +22498,10 @@ function registerConnectionTools(server, api) {
21405
22498
  __name(registerConnectionTools, "registerConnectionTools");
21406
22499
 
21407
22500
  // src/tools/actors.ts
21408
- import { z as z78 } from "zod";
22501
+ import { z as z82 } from "zod";
21409
22502
  var listActorsGuidance = getProductMakerCapabilityModelGuidance("list_actors");
21410
22503
  function registerActorTools(server, api) {
21411
- 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.", z78.object({}).shape, READS_INTERNAL, async () => {
22504
+ 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.", z82.object({}).shape, READS_INTERNAL, async () => {
21412
22505
  try {
21413
22506
  const rows = await api.getJson("/v1/actors");
21414
22507
  const actors = (Array.isArray(rows) ? rows : []).filter((actor) => actor.status === "ready").map(({ id, name, description, gender, voiceId }) => ({
@@ -21429,40 +22522,7 @@ function registerActorTools(server, api) {
21429
22522
  __name(registerActorTools, "registerActorTools");
21430
22523
 
21431
22524
  // src/tools/publish.ts
21432
- import { z as z79 } from "zod";
21433
-
21434
- // src/tools/stable-uuid.ts
21435
- import { createHash as createHash3 } from "crypto";
21436
- function canonicalize(value) {
21437
- if (Array.isArray(value)) return value.map(canonicalize);
21438
- if (value === null || typeof value !== "object") return value;
21439
- const record = value;
21440
- return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
21441
- key,
21442
- canonicalize(record[key])
21443
- ]));
21444
- }
21445
- __name(canonicalize, "canonicalize");
21446
- function canonicalJson(value) {
21447
- return JSON.stringify(canonicalize(value));
21448
- }
21449
- __name(canonicalJson, "canonicalJson");
21450
- function deriveUuidV5(name, namespace) {
21451
- const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
21452
- const digest = createHash3("sha1").update(namespaceBytes).update(name, "utf8").digest();
21453
- const bytes = Buffer.from(digest.subarray(0, 16));
21454
- bytes[6] = (bytes[6] ?? 0) & 15 | 80;
21455
- bytes[8] = (bytes[8] ?? 0) & 63 | 128;
21456
- const hex = bytes.toString("hex");
21457
- return [
21458
- hex.slice(0, 8),
21459
- hex.slice(8, 12),
21460
- hex.slice(12, 16),
21461
- hex.slice(16, 20),
21462
- hex.slice(20)
21463
- ].join("-");
21464
- }
21465
- __name(deriveUuidV5, "deriveUuidV5");
22525
+ import { z as z83 } from "zod";
21466
22526
 
21467
22527
  // src/tools/meta-publish-attempt-id.ts
21468
22528
  var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
@@ -21501,40 +22561,40 @@ var publishToShopifyGuidance = getProductMakerCapabilityModelGuidance("publish_t
21501
22561
  var publishToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_to_meta");
21502
22562
  var publishCreativesToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_creatives_to_meta");
21503
22563
  var publishToTikTokGuidance = getProductMakerCapabilityModelGuidance("publish_to_tiktok");
21504
- var ShopifyInput = z79.object({
21505
- taskId: z79.string().min(1),
21506
- shopDomain: z79.string().min(1),
22564
+ var ShopifyInput = z83.object({
22565
+ taskId: z83.string().min(1),
22566
+ shopDomain: z83.string().min(1),
21507
22567
  /** Override: number of landing images to include. Default = ALL images the
21508
22568
  * task generated (the MCP fetches /v1/results first to count). Bounded
21509
22569
  * generously here; the backend validates each index against
21510
22570
  * landing.images.length and rejects out-of-range. */
21511
- numLandingImages: z79.number().int().min(1).max(20).optional(),
21512
- variantIndex: z79.number().int().min(0).optional(),
22571
+ numLandingImages: z83.number().int().min(1).max(20).optional(),
22572
+ variantIndex: z83.number().int().min(0).optional(),
21513
22573
  /** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
21514
22574
  * When true, the landing renders variant-picker pills; when false, it ships
21515
22575
  * a single-option product page. Defaults to false. */
21516
- showVariantPicker: z79.boolean().optional(),
22576
+ showVariantPicker: z83.boolean().optional(),
21517
22577
  /** Default `auto` updates the product previously published for this task and
21518
22578
  * shop. Use `existing` with targetProductId to choose another product, or
21519
22579
  * `new` only when the user explicitly wants a duplicate/new listing. */
21520
- publicationMode: z79.enum([
22580
+ publicationMode: z83.enum([
21521
22581
  "auto",
21522
22582
  "existing",
21523
22583
  "new"
21524
22584
  ]).optional(),
21525
22585
  /** Shopify product GID, numeric id, or admin product URL. Required when
21526
22586
  * publicationMode is `existing`. */
21527
- targetProductId: z79.string().trim().min(1).max(300).optional(),
22587
+ targetProductId: z83.string().trim().min(1).max(300).optional(),
21528
22588
  /** Add an announcement bar text above add-to-cart (optional). */
21529
- announcementBarEnabled: z79.boolean().optional(),
21530
- announcementBarText: z79.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
22589
+ announcementBarEnabled: z83.boolean().optional(),
22590
+ announcementBarText: z83.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
21531
22591
  commerceCorrection: PublishCommerceCorrectionSchema.optional().describe("Complete seller-confirmed commerce values, with the revision and fingerprint returned in recovery. Never invent missing prices or convert currency."),
21532
- confirmed: z79.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
22592
+ confirmed: z83.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
21533
22593
  });
21534
22594
  var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value, ctx) => {
21535
22595
  if (value.commerceCorrection && value.confirmed !== true) {
21536
22596
  ctx.addIssue({
21537
- code: z79.ZodIssueCode.custom,
22597
+ code: z83.ZodIssueCode.custom,
21538
22598
  path: [
21539
22599
  "confirmed"
21540
22600
  ],
@@ -21543,7 +22603,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
21543
22603
  }
21544
22604
  if (value.announcementBarEnabled && !value.announcementBarText) {
21545
22605
  ctx.addIssue({
21546
- code: z79.ZodIssueCode.custom,
22606
+ code: z83.ZodIssueCode.custom,
21547
22607
  path: [
21548
22608
  "announcementBarText"
21549
22609
  ],
@@ -21552,7 +22612,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
21552
22612
  }
21553
22613
  if (!value.announcementBarEnabled && value.announcementBarText) {
21554
22614
  ctx.addIssue({
21555
- code: z79.ZodIssueCode.custom,
22615
+ code: z83.ZodIssueCode.custom,
21556
22616
  path: [
21557
22617
  "announcementBarText"
21558
22618
  ],
@@ -21566,91 +22626,91 @@ function parseShopifyInput(rawInput) {
21566
22626
  return parsed.data;
21567
22627
  }
21568
22628
  __name(parseShopifyInput, "parseShopifyInput");
21569
- var McpCampaignConfig = z79.object({
22629
+ var McpCampaignConfig = z83.object({
21570
22630
  /** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
21571
22631
  * converts to the account currency server-side. Do NOT pre-convert: on a COP
21572
22632
  * account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
21573
- dailyBudgetUsd: z79.number().positive().optional(),
22633
+ dailyBudgetUsd: z83.number().positive().optional(),
21574
22634
  /** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
21575
22635
  * normalizes at validation). Meta accepts max 1 (single-country ad set).
21576
22636
  * TikTok takes only the first entry. */
21577
- targetCountries: z79.array(CountryIso2Schema).max(1).optional(),
21578
- campaignName: z79.string().trim().min(1).max(100).optional()
22637
+ targetCountries: z83.array(CountryIso2Schema).max(1).optional(),
22638
+ campaignName: z83.string().trim().min(1).max(100).optional()
21579
22639
  });
21580
22640
  var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
21581
- var MetaInput = z79.object({
21582
- taskId: z79.string().min(1),
22641
+ var MetaInput = z83.object({
22642
+ taskId: z83.string().min(1),
21583
22643
  /** Exact Meta Business portfolio selected from `list_meta_destinations`. */
21584
- portfolioId: z79.string().trim().min(1),
22644
+ portfolioId: z83.string().trim().min(1),
21585
22645
  /** Exact Facebook Page selected from `list_meta_destinations`. */
21586
- pageId: z79.string().trim().min(1),
22646
+ pageId: z83.string().trim().min(1),
21587
22647
  adAccountId: McpAdAccountId,
21588
- pixelId: z79.string().min(1).optional(),
21589
- landingUrl: z79.string().url().optional(),
22648
+ pixelId: z83.string().min(1).optional(),
22649
+ landingUrl: z83.string().url().optional(),
21590
22650
  /** Select which angles to publish by 0-based index. Omit to publish all
21591
22651
  * successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
21592
- angleIndexes: z79.array(z79.number().int().nonnegative()).optional(),
22652
+ angleIndexes: z83.array(z83.number().int().nonnegative()).optional(),
21593
22653
  campaignConfig: McpCampaignConfig.optional(),
21594
22654
  /** Override the ad's primary text (body copy). If omitted, ProductMaker
21595
22655
  * uses the auto-generated copy attached to the task. Capped at 500 chars
21596
22656
  * (Meta's primary-text limit). */
21597
- primaryText: z79.string().trim().min(1).max(500).optional(),
22657
+ primaryText: z83.string().trim().min(1).max(500).optional(),
21598
22658
  /** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
21599
22659
  * (wa.me) instead of a landing page. */
21600
- phoneNumber: z79.string().min(8).max(32).optional()
22660
+ phoneNumber: z83.string().min(8).max(32).optional()
21601
22661
  });
21602
- var GroupedTaskSource = z79.object({
21603
- type: z79.literal("task"),
21604
- taskId: z79.string().min(1),
21605
- angleIndex: z79.number().int().min(0).optional(),
21606
- videoVariant: z79.enum([
22662
+ var GroupedTaskSource = z83.object({
22663
+ type: z83.literal("task"),
22664
+ taskId: z83.string().min(1),
22665
+ angleIndex: z83.number().int().min(0).optional(),
22666
+ videoVariant: z83.enum([
21607
22667
  "with_subtitles",
21608
22668
  "without_subtitles"
21609
22669
  ]).optional(),
21610
- imageVariantIndex: z79.number().int().min(0).optional()
21611
- });
21612
- var GroupedMediaSource = z79.object({
21613
- type: z79.literal("media"),
21614
- url: z79.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
21615
- path: z79.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".'),
21616
- thumbnailUrl: z79.string().url().optional(),
21617
- thumbnailPath: z79.string().min(1).optional()
22670
+ imageVariantIndex: z83.number().int().min(0).optional()
22671
+ });
22672
+ var GroupedMediaSource = z83.object({
22673
+ type: z83.literal("media"),
22674
+ url: z83.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
22675
+ path: z83.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".'),
22676
+ thumbnailUrl: z83.string().url().optional(),
22677
+ thumbnailPath: z83.string().min(1).optional()
21618
22678
  }).refine((value) => Boolean(value.url) !== Boolean(value.path), {
21619
22679
  message: "media source needs exactly one of `url` or `path`"
21620
22680
  }).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
21621
22681
  message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
21622
22682
  });
21623
- var GroupedUploadedAssetSource = z79.object({
21624
- type: z79.literal("asset"),
22683
+ var GroupedUploadedAssetSource = z83.object({
22684
+ type: z83.literal("asset"),
21625
22685
  /** Returned by check_media_upload. ProductMaker resolves ownership server-side. */
21626
- assetId: z79.string().uuid(),
21627
- thumbnailAssetId: z79.string().uuid().optional()
22686
+ assetId: z83.string().uuid(),
22687
+ thumbnailAssetId: z83.string().uuid().optional()
21628
22688
  });
21629
- var GroupedCreative = z79.object({
21630
- type: z79.enum([
22689
+ var GroupedCreative = z83.object({
22690
+ type: z83.enum([
21631
22691
  "image",
21632
22692
  "video"
21633
22693
  ]),
21634
- source: z79.union([
22694
+ source: z83.union([
21635
22695
  GroupedTaskSource,
21636
22696
  GroupedMediaSource,
21637
22697
  GroupedUploadedAssetSource
21638
22698
  ]),
21639
- title: z79.string().max(120).optional(),
21640
- description: z79.string().max(500).optional(),
22699
+ title: z83.string().max(120).optional(),
22700
+ description: z83.string().max(500).optional(),
21641
22701
  cta: MetaCtaType.optional(),
21642
- creativeId: z79.string().min(1).optional()
22702
+ creativeId: z83.string().min(1).optional()
21643
22703
  });
21644
- var GroupedCampaignConfigBase = z79.object({
22704
+ var GroupedCampaignConfigBase = z83.object({
21645
22705
  campaignType: MetaCampaignType.default("sales"),
21646
22706
  optimizationMode: MetaOptimizationMode.default("advantage_plus"),
21647
22707
  budgetMode: MetaBudgetMode.optional(),
21648
- campaignDailyBudget: z79.number().int().positive().optional(),
21649
- adSets: z79.array(z79.object({
21650
- tempId: z79.string().min(1).max(80),
21651
- name: z79.string().min(1).max(100),
21652
- dailyBudget: z79.number().int().positive().optional(),
21653
- creativeIds: z79.array(z79.string().min(1)).min(1)
22708
+ campaignDailyBudget: z83.number().int().positive().optional(),
22709
+ adSets: z83.array(z83.object({
22710
+ tempId: z83.string().min(1).max(80),
22711
+ name: z83.string().min(1).max(100),
22712
+ dailyBudget: z83.number().int().positive().optional(),
22713
+ creativeIds: z83.array(z83.string().min(1)).min(1)
21654
22714
  })).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
21655
22715
  /**
21656
22716
  * Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
@@ -21658,17 +22718,17 @@ var GroupedCampaignConfigBase = z79.object({
21658
22718
  * opaque `invalid_literal` and made the WhatsApp destination — supported by
21659
22719
  * the API since #849 — unreachable from this tool.
21660
22720
  */
21661
- destination: z79.discriminatedUnion("type", [
21662
- z79.object({
21663
- type: z79.literal("landing"),
21664
- landingUrl: z79.string().url()
22721
+ destination: z83.discriminatedUnion("type", [
22722
+ z83.object({
22723
+ type: z83.literal("landing"),
22724
+ landingUrl: z83.string().url()
21665
22725
  }),
21666
- z79.object({
21667
- type: z79.literal("whatsapp"),
21668
- phoneNumber: z79.string().min(8).max(32)
22726
+ z83.object({
22727
+ type: z83.literal("whatsapp"),
22728
+ phoneNumber: z83.string().min(8).max(32)
21669
22729
  })
21670
22730
  ]),
21671
- saveAsDefault: z79.boolean().default(false),
22731
+ saveAsDefault: z83.boolean().default(false),
21672
22732
  budgetPolicy: MetaBudgetPolicy.optional()
21673
22733
  });
21674
22734
  function resolveBudgetMode(value) {
@@ -21682,7 +22742,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
21682
22742
  const budgetMode = resolveBudgetMode(value);
21683
22743
  if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
21684
22744
  ctx.addIssue({
21685
- code: z79.ZodIssueCode.custom,
22745
+ code: z83.ZodIssueCode.custom,
21686
22746
  path: [
21687
22747
  "campaignDailyBudget"
21688
22748
  ],
@@ -21693,7 +22753,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
21693
22753
  value.adSets.forEach((adSet, index) => {
21694
22754
  if (adSet.dailyBudget === void 0) {
21695
22755
  ctx.addIssue({
21696
- code: z79.ZodIssueCode.custom,
22756
+ code: z83.ZodIssueCode.custom,
21697
22757
  path: [
21698
22758
  "adSets",
21699
22759
  index,
@@ -21708,28 +22768,28 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
21708
22768
  ...value,
21709
22769
  budgetMode: resolveBudgetMode(value)
21710
22770
  }));
21711
- var PublishCreativesToMetaInputBase = z79.object({
21712
- creatives: z79.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
22771
+ var PublishCreativesToMetaInputBase = z83.object({
22772
+ creatives: z83.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
21713
22773
  /** Exact Meta Business portfolio selected from `list_meta_destinations`. */
21714
- portfolioId: z79.string().trim().min(1),
22774
+ portfolioId: z83.string().trim().min(1),
21715
22775
  /** Exact Facebook Page selected from `list_meta_destinations`. */
21716
- pageId: z79.string().trim().min(1),
22776
+ pageId: z83.string().trim().min(1),
21717
22777
  adAccountId: McpAdAccountId,
21718
- pixelId: z79.string().min(1).optional(),
22778
+ pixelId: z83.string().min(1).optional(),
21719
22779
  /** Required unless `campaignConfig.destination` is whatsapp — see the
21720
22780
  * superRefine below. Mirrors the API's `FacebookCreateCampaignFromTasksBodySchema`. */
21721
- landingUrl: z79.string().url().optional(),
22781
+ landingUrl: z83.string().url().optional(),
21722
22782
  /** ISO-2 code or resolvable country name — mirrors the API contract
21723
22783
  * (CountryIso2Schema), which normalizes names to alpha-2 at validation. */
21724
22784
  targetCountry: CountryIso2Schema,
21725
- campaignName: z79.string().trim().min(1).max(100),
22785
+ campaignName: z83.string().trim().min(1).max(100),
21726
22786
  campaignConfig: GroupedCampaignConfig.optional()
21727
22787
  });
21728
22788
  var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
21729
22789
  const destination = value.campaignConfig?.destination;
21730
22790
  if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl) {
21731
22791
  ctx.addIssue({
21732
- code: z79.ZodIssueCode.custom,
22792
+ code: z83.ZodIssueCode.custom,
21733
22793
  path: [
21734
22794
  "landingUrl"
21735
22795
  ],
@@ -21738,7 +22798,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
21738
22798
  }
21739
22799
  if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
21740
22800
  ctx.addIssue({
21741
- code: z79.ZodIssueCode.custom,
22801
+ code: z83.ZodIssueCode.custom,
21742
22802
  path: [
21743
22803
  "campaignConfig",
21744
22804
  "destination",
@@ -21757,16 +22817,16 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
21757
22817
  }
21758
22818
  return value;
21759
22819
  });
21760
- var PresignUploadResponseSchema = z79.object({
21761
- uploadUrl: z79.string().url().refine((value) => {
22820
+ var PresignUploadResponseSchema = z83.object({
22821
+ uploadUrl: z83.string().url().refine((value) => {
21762
22822
  const url = new URL(value);
21763
22823
  return !url.username && !url.password && url.protocol === "https:";
21764
22824
  }, "presigned upload URL must be HTTPS without embedded credentials"),
21765
- publicUrl: z79.string().url().refine((value) => {
22825
+ publicUrl: z83.string().url().refine((value) => {
21766
22826
  const url = new URL(value);
21767
22827
  return !url.username && !url.password;
21768
22828
  }, "public media URL must not contain embedded credentials"),
21769
- stagingAssetId: z79.string().uuid()
22829
+ stagingAssetId: z83.string().uuid()
21770
22830
  });
21771
22831
  async function uploadResolvedMedia(api, media, assetRole) {
21772
22832
  if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
@@ -21791,16 +22851,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
21791
22851
  return uploadResolvedMedia(api, media, assetRole);
21792
22852
  }
21793
22853
  __name(resolveAndUploadMedia, "resolveAndUploadMedia");
21794
- var TikTokInput = z79.object({
21795
- taskId: z79.string().min(1),
21796
- advertiserId: z79.string().min(1),
21797
- pixelId: z79.string().min(1).optional(),
21798
- landingUrl: z79.string().url().optional(),
21799
- angleIndexes: z79.array(z79.number().int().nonnegative()).optional(),
22854
+ var TikTokInput = z83.object({
22855
+ taskId: z83.string().min(1),
22856
+ advertiserId: z83.string().min(1),
22857
+ pixelId: z83.string().min(1).optional(),
22858
+ landingUrl: z83.string().url().optional(),
22859
+ angleIndexes: z83.array(z83.number().int().nonnegative()).optional(),
21800
22860
  campaignConfig: McpCampaignConfig.optional(),
21801
22861
  /** Override the ad's description (TikTok's body copy). If omitted,
21802
22862
  * ProductMaker uses the auto-generated copy attached to the task. */
21803
- primaryText: z79.string().trim().min(1).max(500).optional()
22863
+ primaryText: z83.string().trim().min(1).max(500).optional()
21804
22864
  });
21805
22865
  function registerPublishTools(server, api, opts = {}) {
21806
22866
  const transport = opts.transport ?? "http";
@@ -22128,39 +23188,39 @@ __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
22128
23188
 
22129
23189
  // src/tools/meta-upload.ts
22130
23190
  import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
22131
- import { z as z80 } from "zod";
23191
+ import { z as z84 } from "zod";
22132
23192
  var requestMediaUploadGuidance = getProductMakerCapabilityModelGuidance("request_media_upload");
22133
- var UploadSessionInputBase = z80.object({
22134
- mediaKind: z80.enum([
23193
+ var UploadSessionInputBase = z84.object({
23194
+ mediaKind: z84.enum([
22135
23195
  "image",
22136
23196
  "video",
22137
23197
  "mixed"
22138
23198
  ]).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."),
22139
- assetRole: z80.enum([
23199
+ assetRole: z84.enum([
22140
23200
  "media",
22141
23201
  "thumbnail"
22142
23202
  ]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail."),
22143
- uploadPurpose: z80.enum([
23203
+ uploadPurpose: z84.enum([
22144
23204
  "product_reference",
22145
23205
  "creative",
22146
23206
  "generic"
22147
23207
  ]).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."),
22148
- maxFiles: z80.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
23208
+ maxFiles: z84.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
22149
23209
  });
22150
- var OpenAiFileReference = z80.object({
22151
- download_url: z80.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."),
22152
- file_id: z80.string().min(1),
22153
- mime_type: z80.string().min(1).optional(),
22154
- file_name: z80.string().min(1).optional()
23210
+ var OpenAiFileReference = z84.object({
23211
+ download_url: z84.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."),
23212
+ file_id: z84.string().min(1),
23213
+ mime_type: z84.string().min(1).optional(),
23214
+ file_name: z84.string().min(1).optional()
22155
23215
  });
22156
23216
  var UploadSessionToolInputShape = {
22157
23217
  ...UploadSessionInputBase.shape,
22158
- files: z80.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.")
23218
+ files: z84.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.")
22159
23219
  };
22160
- var UploadSessionToolInput = z80.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
23220
+ var UploadSessionToolInput = z84.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
22161
23221
  if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
22162
23222
  ctx.addIssue({
22163
- code: z80.ZodIssueCode.custom,
23223
+ code: z84.ZodIssueCode.custom,
22164
23224
  path: [
22165
23225
  "mediaKind"
22166
23226
  ],
@@ -22169,7 +23229,7 @@ var UploadSessionToolInput = z80.object(UploadSessionToolInputShape).superRefine
22169
23229
  }
22170
23230
  if (value.mediaKind === "video" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
22171
23231
  ctx.addIssue({
22172
- code: z80.ZodIssueCode.custom,
23232
+ code: z84.ZodIssueCode.custom,
22173
23233
  path: [
22174
23234
  "maxFiles"
22175
23235
  ],
@@ -22178,7 +23238,7 @@ var UploadSessionToolInput = z80.object(UploadSessionToolInputShape).superRefine
22178
23238
  }
22179
23239
  if (value.mediaKind === "image" && (value.maxFiles ?? 1) > 5) {
22180
23240
  ctx.addIssue({
22181
- code: z80.ZodIssueCode.custom,
23241
+ code: z84.ZodIssueCode.custom,
22182
23242
  path: [
22183
23243
  "maxFiles"
22184
23244
  ],
@@ -22186,38 +23246,38 @@ var UploadSessionToolInput = z80.object(UploadSessionToolInputShape).superRefine
22186
23246
  });
22187
23247
  }
22188
23248
  });
22189
- var UploadSessionResponse = z80.object({
22190
- sessionId: z80.string().uuid(),
22191
- uploadPageUrl: z80.string().url(),
22192
- mediaKind: z80.enum([
23249
+ var UploadSessionResponse = z84.object({
23250
+ sessionId: z84.string().uuid(),
23251
+ uploadPageUrl: z84.string().url(),
23252
+ mediaKind: z84.enum([
22193
23253
  "image",
22194
23254
  "video",
22195
23255
  "mixed"
22196
23256
  ]),
22197
- assetRole: z80.enum([
23257
+ assetRole: z84.enum([
22198
23258
  "media",
22199
23259
  "thumbnail"
22200
23260
  ]),
22201
- maxFiles: z80.number().int().positive(),
22202
- expiresAt: z80.string().datetime()
23261
+ maxFiles: z84.number().int().positive(),
23262
+ expiresAt: z84.string().datetime()
22203
23263
  });
22204
- var UploadStatusInput = z80.object({
22205
- sessionId: z80.string().uuid().describe("The sessionId returned by request_media_upload.")
23264
+ var UploadStatusInput = z84.object({
23265
+ sessionId: z84.string().uuid().describe("The sessionId returned by request_media_upload.")
22206
23266
  });
22207
- var UniversalUploadStatusResponse = z80.discriminatedUnion("status", [
22208
- z80.object({
22209
- status: z80.literal("pending"),
22210
- assets: z80.array(z80.never()).length(0)
23267
+ var UniversalUploadStatusResponse = z84.discriminatedUnion("status", [
23268
+ z84.object({
23269
+ status: z84.literal("pending"),
23270
+ assets: z84.array(z84.never()).length(0)
22211
23271
  }),
22212
- z80.object({
22213
- status: z80.literal("uploaded"),
22214
- assets: z80.array(z80.object({
22215
- assetId: z80.string().uuid(),
22216
- mediaKind: z80.enum([
23272
+ z84.object({
23273
+ status: z84.literal("uploaded"),
23274
+ assets: z84.array(z84.object({
23275
+ assetId: z84.string().uuid(),
23276
+ mediaKind: z84.enum([
22217
23277
  "image",
22218
23278
  "video"
22219
23279
  ]),
22220
- assetRole: z80.enum([
23280
+ assetRole: z84.enum([
22221
23281
  "media",
22222
23282
  "thumbnail"
22223
23283
  ])
@@ -22225,13 +23285,13 @@ var UniversalUploadStatusResponse = z80.discriminatedUnion("status", [
22225
23285
  })
22226
23286
  ]);
22227
23287
  var UniversalUploadStructuredResponse = UploadSessionResponse.extend({
22228
- uploadPurpose: z80.enum([
23288
+ uploadPurpose: z84.enum([
22229
23289
  "product_reference",
22230
23290
  "creative",
22231
23291
  "generic"
22232
23292
  ]),
22233
- checkToolName: z80.literal("check_media_upload"),
22234
- resolveEndpoint: z80.string().url()
23293
+ checkToolName: z84.literal("check_media_upload"),
23294
+ resolveEndpoint: z84.string().url()
22235
23295
  });
22236
23296
  function contextualizeUploadPageUrl(uploadPageUrl, uploadPurpose) {
22237
23297
  const url = new URL(uploadPageUrl);
@@ -22322,15 +23382,15 @@ function registerMediaUploadTools(server, api) {
22322
23382
  __name(registerMediaUploadTools, "registerMediaUploadTools");
22323
23383
 
22324
23384
  // src/tools/campaigns.ts
22325
- import { z as z81 } from "zod";
23385
+ import { z as z85 } from "zod";
22326
23386
  var getMetaCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_meta_campaign_status");
22327
23387
  var getTikTokCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_tiktok_campaign_status");
22328
- var MetaStatusInput = z81.object({
22329
- campaignId: z81.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
23388
+ var MetaStatusInput = z85.object({
23389
+ campaignId: z85.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
22330
23390
  });
22331
- var TikTokStatusInput = z81.object({
22332
- campaignId: z81.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
22333
- advertiserId: z81.string().min(1).describe("The same advertiserId used to publish the campaign.")
23391
+ var TikTokStatusInput = z85.object({
23392
+ campaignId: z85.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
23393
+ advertiserId: z85.string().min(1).describe("The same advertiserId used to publish the campaign.")
22334
23394
  });
22335
23395
  function registerCampaignTools(server, api) {
22336
23396
  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) => {
@@ -22427,10 +23487,10 @@ function renderTikTokStatusMarkdown(r) {
22427
23487
  __name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
22428
23488
 
22429
23489
  // ../../packages/product-economics/src/product-costing.ts
22430
- import { z as z83 } from "zod";
23490
+ import { z as z87 } from "zod";
22431
23491
 
22432
23492
  // ../../packages/product-economics/src/product-costing-countries.ts
22433
- import { z as z82 } from "zod";
23493
+ import { z as z86 } from "zod";
22434
23494
 
22435
23495
  // ../../packages/product-economics/src/product-costing-shipping-references.json
22436
23496
  var product_costing_shipping_references_default = {
@@ -22494,21 +23554,21 @@ var product_costing_shipping_references_default = {
22494
23554
  };
22495
23555
 
22496
23556
  // ../../packages/product-economics/src/product-costing-countries.ts
22497
- var ShippingReferenceSchema = z82.object({
22498
- reference: z82.number().positive(),
22499
- min: z82.number().nonnegative(),
22500
- max: z82.number().positive(),
22501
- updatedAt: z82.string().date(),
22502
- confidence: z82.enum([
23557
+ var ShippingReferenceSchema = z86.object({
23558
+ reference: z86.number().positive(),
23559
+ min: z86.number().nonnegative(),
23560
+ max: z86.number().positive(),
23561
+ updatedAt: z86.string().date(),
23562
+ confidence: z86.enum([
22503
23563
  "high",
22504
23564
  "medium",
22505
23565
  "medium-low"
22506
23566
  ]),
22507
- sources: z82.array(z82.string().url().startsWith("https://")).min(1).max(3)
23567
+ sources: z86.array(z86.string().url().startsWith("https://")).min(1).max(3)
22508
23568
  }).superRefine(({ reference, min, max }, context) => {
22509
23569
  if (min > reference || reference > max) {
22510
23570
  context.addIssue({
22511
- code: z82.ZodIssueCode.custom,
23571
+ code: z86.ZodIssueCode.custom,
22512
23572
  message: "shipping_reference_outside_range",
22513
23573
  path: [
22514
23574
  "reference"
@@ -22516,7 +23576,7 @@ var ShippingReferenceSchema = z82.object({
22516
23576
  });
22517
23577
  }
22518
23578
  });
22519
- var SHIPPING_REFERENCES = z82.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
23579
+ var SHIPPING_REFERENCES = z86.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
22520
23580
  var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
22521
23581
  var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
22522
23582
  "MX",
@@ -22698,18 +23758,18 @@ function getProductCostingCountryName(country) {
22698
23758
  __name(getProductCostingCountryName, "getProductCostingCountryName");
22699
23759
 
22700
23760
  // ../../packages/product-economics/src/product-costing.ts
22701
- var ProductCostingInputSchema = z83.object({
22702
- country: z83.string().refine(isProductCostingCountryCode, "unsupported_country"),
22703
- supplierCost: z83.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
22704
- shippingCost: z83.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
22705
- returnRate: z83.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
22706
- cancellationRate: z83.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
22707
- adsManagerCpa: z83.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
22708
- desiredMargin: z83.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
23761
+ var ProductCostingInputSchema = z87.object({
23762
+ country: z87.string().refine(isProductCostingCountryCode, "unsupported_country"),
23763
+ supplierCost: z87.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
23764
+ shippingCost: z87.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
23765
+ returnRate: z87.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
23766
+ cancellationRate: z87.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
23767
+ adsManagerCpa: z87.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
23768
+ desiredMargin: z87.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
22709
23769
  }).superRefine(({ returnRate, cancellationRate }, context) => {
22710
23770
  if (returnRate + cancellationRate >= 1) {
22711
23771
  context.addIssue({
22712
- code: z83.ZodIssueCode.custom,
23772
+ code: z87.ZodIssueCode.custom,
22713
23773
  message: "delivery_rate_positive",
22714
23774
  path: [
22715
23775
  "cancellationRate"
@@ -22806,15 +23866,15 @@ function calculateProductCosting(rawInput) {
22806
23866
  __name(calculateProductCosting, "calculateProductCosting");
22807
23867
 
22808
23868
  // src/tools/product-costing.ts
22809
- import { z as z84 } from "zod";
23869
+ import { z as z88 } from "zod";
22810
23870
  var costProductGuidance = getProductMakerCapabilityModelGuidance("cost_product");
22811
- var optionalAmount = /* @__PURE__ */ __name((description) => z84.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
22812
- var optionalPercent = /* @__PURE__ */ __name((description) => z84.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
22813
- var ProductCostingToolInput = z84.object({
22814
- country: z84.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
23871
+ var optionalAmount = /* @__PURE__ */ __name((description) => z88.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
23872
+ var optionalPercent = /* @__PURE__ */ __name((description) => z88.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
23873
+ var ProductCostingToolInput = z88.object({
23874
+ country: z88.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
22815
23875
  message: "Pa\xEDs no soportado por ProductMaker."
22816
23876
  }).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
22817
- supplierCost: z84.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."),
23877
+ supplierCost: z88.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."),
22818
23878
  supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
22819
23879
  shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
22820
23880
  shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
@@ -22973,7 +24033,7 @@ function formatPercent(value) {
22973
24033
  __name(formatPercent, "formatPercent");
22974
24034
 
22975
24035
  // src/tools/product-research.ts
22976
- import { z as z85 } from "zod";
24036
+ import { z as z89 } from "zod";
22977
24037
 
22978
24038
  // src/tools/product-research-idempotency.ts
22979
24039
  var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
@@ -23344,35 +24404,35 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
23344
24404
  ]);
23345
24405
  var POLL_INTERVAL_MS = 5e3;
23346
24406
  var MAX_WAIT_SECONDS = 120;
23347
- var FindInput = z85.object({
24407
+ var FindInput = z89.object({
23348
24408
  targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
23349
24409
  categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
23350
- confirmed: z85.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
23351
- waitSeconds: z85.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
24410
+ confirmed: z89.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
24411
+ waitSeconds: z89.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
23352
24412
  });
23353
- var GetInput = z85.object({
23354
- runId: z85.string().uuid().describe("ID del run devuelto por find_winning_products.")
24413
+ var GetInput = z89.object({
24414
+ runId: z89.string().uuid().describe("ID del run devuelto por find_winning_products.")
23355
24415
  });
23356
- var ListInput = z85.object({
23357
- limit: z85.number().int().min(1).max(20).default(10)
24416
+ var ListInput = z89.object({
24417
+ limit: z89.number().int().min(1).max(20).default(10)
23358
24418
  });
23359
- function textResult(text) {
24419
+ function textResult(text2) {
23360
24420
  return {
23361
24421
  content: [
23362
24422
  {
23363
24423
  type: "text",
23364
- text
24424
+ text: text2
23365
24425
  }
23366
24426
  ]
23367
24427
  };
23368
24428
  }
23369
24429
  __name(textResult, "textResult");
23370
- function researchResult(run, text) {
24430
+ function researchResult(run, text2) {
23371
24431
  return {
23372
24432
  content: [
23373
24433
  {
23374
24434
  type: "text",
23375
- text
24435
+ text: text2
23376
24436
  }
23377
24437
  ],
23378
24438
  structuredContent: run
@@ -23477,17 +24537,17 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
23477
24537
  __name(registerProductResearchTools, "registerProductResearchTools");
23478
24538
 
23479
24539
  // src/tools/dropshipping-projects.ts
23480
- import { z as z86 } from "zod";
24540
+ import { z as z90 } from "zod";
23481
24541
  var startDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("start_dropshipping_project");
23482
24542
  var costDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("cost_dropshipping_project");
23483
24543
  var guideDropshippingLaunchGuidance = getProductMakerCapabilityModelGuidance("guide_dropshipping_launch");
23484
- var ProjectId = z86.string().uuid();
23485
- var DropshippingProjectStatus = z86.enum([
24544
+ var ProjectId = z90.string().uuid();
24545
+ var DropshippingProjectStatus = z90.enum([
23486
24546
  "active",
23487
24547
  "completed",
23488
24548
  "archived"
23489
24549
  ]);
23490
- var DropshippingProjectPhase = z86.enum([
24550
+ var DropshippingProjectPhase = z90.enum([
23491
24551
  "research",
23492
24552
  "costing",
23493
24553
  "media",
@@ -23496,21 +24556,21 @@ var DropshippingProjectPhase = z86.enum([
23496
24556
  "advertising",
23497
24557
  "complete"
23498
24558
  ]);
23499
- var DropshippingRewardMilestone = z86.enum([
24559
+ var DropshippingRewardMilestone = z90.enum([
23500
24560
  "product_selected",
23501
24561
  "costing_saved",
23502
24562
  "product_media_ready",
23503
24563
  "creatives_completed",
23504
24564
  "campaign_created"
23505
24565
  ]);
23506
- var TargetCountry = z86.enum([
24566
+ var TargetCountry = z90.enum([
23507
24567
  "CO",
23508
24568
  "MX",
23509
24569
  "PE",
23510
24570
  "EC",
23511
24571
  "CL"
23512
24572
  ]);
23513
- var ResearchCategory = z86.enum([
24573
+ var ResearchCategory = z90.enum([
23514
24574
  "auto",
23515
24575
  "automotive",
23516
24576
  "kitchen",
@@ -23525,7 +24585,7 @@ var ResearchCategory = z86.enum([
23525
24585
  "garden",
23526
24586
  "cleaning"
23527
24587
  ]);
23528
- var HttpUrl3 = z86.string().url().max(2e3).refine((value) => {
24588
+ var HttpUrl3 = z90.string().url().max(2e3).refine((value) => {
23529
24589
  try {
23530
24590
  return [
23531
24591
  "http:",
@@ -23535,45 +24595,45 @@ var HttpUrl3 = z86.string().url().max(2e3).refine((value) => {
23535
24595
  return false;
23536
24596
  }
23537
24597
  }, "URL must use HTTP(S)");
23538
- var ResearchCandidateOrigin = z86.object({
23539
- type: z86.literal("research_candidate"),
24598
+ var ResearchCandidateOrigin = z90.object({
24599
+ type: z90.literal("research_candidate"),
23540
24600
  runId: ProjectId,
23541
- candidateSourceId: z86.string().min(1).max(160)
24601
+ candidateSourceId: z90.string().min(1).max(160)
23542
24602
  });
23543
- var TaskOrigin = z86.object({
23544
- type: z86.literal("task"),
24603
+ var TaskOrigin = z90.object({
24604
+ type: z90.literal("task"),
23545
24605
  taskId: ProjectId
23546
24606
  });
23547
- var ManualOrigin = z86.object({
23548
- type: z86.literal("manual"),
24607
+ var ManualOrigin = z90.object({
24608
+ type: z90.literal("manual"),
23549
24609
  clientRequestId: ProjectId,
23550
- country: z86.string().min(2).max(3).optional()
24610
+ country: z90.string().min(2).max(3).optional()
23551
24611
  });
23552
- var StartProjectInput = z86.object({
23553
- origin: z86.discriminatedUnion("type", [
24612
+ var StartProjectInput = z90.object({
24613
+ origin: z90.discriminatedUnion("type", [
23554
24614
  ResearchCandidateOrigin,
23555
24615
  TaskOrigin,
23556
24616
  ManualOrigin
23557
24617
  ]),
23558
- productName: z86.string().trim().min(1).max(240).optional(),
24618
+ productName: z90.string().trim().min(1).max(240).optional(),
23559
24619
  productUrl: HttpUrl3.optional(),
23560
- country: z86.string().min(2).max(3).optional(),
23561
- languageCode: z86.string().min(2).max(16).optional()
24620
+ country: z90.string().min(2).max(3).optional(),
24621
+ languageCode: z90.string().min(2).max(16).optional()
23562
24622
  }).strict();
23563
- var CostProjectInput = z86.object({
24623
+ var CostProjectInput = z90.object({
23564
24624
  projectId: ProjectId,
23565
- version: z86.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
23566
- country: z86.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
24625
+ version: z90.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
24626
+ country: z90.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
23567
24627
  message: "Pa\xEDs no soportado por ProductMaker."
23568
24628
  }).describe("C\xF3digo ISO-2 del pa\xEDs del proyecto. Usa el `targetCountry` que devuelve el proyecto."),
23569
- supplierCost: z86.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."),
23570
- shippingCost: z86.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
23571
- returnRate: z86.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."),
23572
- cancellationRate: z86.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."),
23573
- adsManagerCpa: z86.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
23574
- desiredMargin: z86.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
23575
- });
23576
- var GuideInput = z86.object({
24629
+ supplierCost: z90.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."),
24630
+ shippingCost: z90.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
24631
+ returnRate: z90.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."),
24632
+ cancellationRate: z90.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."),
24633
+ adsManagerCpa: z90.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
24634
+ desiredMargin: z90.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
24635
+ });
24636
+ var GuideInput = z90.object({
23577
24637
  projectId: ProjectId.optional()
23578
24638
  }).strict();
23579
24639
  var NEXT_ACTION_KINDS = [
@@ -23591,29 +24651,29 @@ var NEXT_ACTION_KINDS = [
23591
24651
  "repair_meta_campaign",
23592
24652
  "review_paused_campaign"
23593
24653
  ];
23594
- var DropshippingProjectNextAction = z86.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind) => z86.object({
23595
- kind: z86.literal(kind),
23596
- href: z86.string().min(1)
24654
+ var DropshippingProjectNextAction = z90.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind) => z90.object({
24655
+ kind: z90.literal(kind),
24656
+ href: z90.string().min(1)
23597
24657
  })));
23598
- var CostingInputSchema = z86.object({
23599
- country: z86.string().min(2).max(3),
23600
- supplierCost: z86.number().finite().positive(),
23601
- shippingCost: z86.number().finite().nonnegative(),
23602
- returnRate: z86.number().finite().min(0).lt(1),
23603
- cancellationRate: z86.number().finite().min(0).lt(1),
23604
- adsManagerCpa: z86.number().finite().nonnegative(),
23605
- desiredMargin: z86.number().finite().min(0).lt(1)
24658
+ var CostingInputSchema = z90.object({
24659
+ country: z90.string().min(2).max(3),
24660
+ supplierCost: z90.number().finite().positive(),
24661
+ shippingCost: z90.number().finite().nonnegative(),
24662
+ returnRate: z90.number().finite().min(0).lt(1),
24663
+ cancellationRate: z90.number().finite().min(0).lt(1),
24664
+ adsManagerCpa: z90.number().finite().nonnegative(),
24665
+ desiredMargin: z90.number().finite().min(0).lt(1)
23606
24666
  }).superRefine(({ returnRate, cancellationRate }, context) => {
23607
24667
  if (returnRate + cancellationRate >= 1) context.addIssue({
23608
- code: z86.ZodIssueCode.custom,
24668
+ code: z90.ZodIssueCode.custom,
23609
24669
  path: [
23610
24670
  "cancellationRate"
23611
24671
  ]
23612
24672
  });
23613
24673
  });
23614
- var FiniteCostingNumber = z86.number().finite();
23615
- var CostingResultSchema = z86.object({
23616
- country: z86.string().min(2).max(3),
24674
+ var FiniteCostingNumber = z90.number().finite();
24675
+ var CostingResultSchema = z90.object({
24676
+ country: z90.string().min(2).max(3),
23617
24677
  deliveryRate: FiniteCostingNumber,
23618
24678
  adjustedShippingCost: FiniteCostingNumber,
23619
24679
  adjustedCpa: FiniteCostingNumber,
@@ -23626,45 +24686,45 @@ var CostingResultSchema = z86.object({
23626
24686
  estimatedProfit: FiniteCostingNumber,
23627
24687
  actualMargin: FiniteCostingNumber,
23628
24688
  breakEvenAdsManagerCpa: FiniteCostingNumber,
23629
- wasPriceAutoAdjusted: z86.boolean(),
23630
- profitabilityStatus: z86.literal("healthy")
23631
- });
23632
- var RewardProgressSchema = z86.object({
23633
- campaignKey: z86.literal("dropshipping-launch-v1"),
23634
- totalCredits: z86.number().int().positive(),
23635
- earnedCredits: z86.number().int().nonnegative(),
23636
- milestones: z86.array(z86.object({
24689
+ wasPriceAutoAdjusted: z90.boolean(),
24690
+ profitabilityStatus: z90.literal("healthy")
24691
+ });
24692
+ var RewardProgressSchema = z90.object({
24693
+ campaignKey: z90.literal("dropshipping-launch-v1"),
24694
+ totalCredits: z90.number().int().positive(),
24695
+ earnedCredits: z90.number().int().nonnegative(),
24696
+ milestones: z90.array(z90.object({
23637
24697
  milestone: DropshippingRewardMilestone,
23638
- credits: z86.number().int().positive(),
23639
- available: z86.boolean().optional(),
23640
- earned: z86.boolean(),
24698
+ credits: z90.number().int().positive(),
24699
+ available: z90.boolean().optional(),
24700
+ earned: z90.boolean(),
23641
24701
  grantId: ProjectId.nullable(),
23642
- grantedAt: z86.string().datetime().nullable()
24702
+ grantedAt: z90.string().datetime().nullable()
23643
24703
  })).length(DropshippingRewardMilestone.options.length)
23644
24704
  });
23645
- var ProjectResponseSchema = z86.object({
24705
+ var ProjectResponseSchema = z90.object({
23646
24706
  id: ProjectId,
23647
24707
  taskId: ProjectId.nullable(),
23648
24708
  status: DropshippingProjectStatus,
23649
- originKey: z86.string().min(1),
24709
+ originKey: z90.string().min(1),
23650
24710
  researchRunId: ProjectId.nullable(),
23651
- candidateSourceId: z86.string().nullable(),
23652
- productName: z86.string().nullable(),
24711
+ candidateSourceId: z90.string().nullable(),
24712
+ productName: z90.string().nullable(),
23653
24713
  productUrl: HttpUrl3.nullable(),
23654
24714
  referenceImageUrl: HttpUrl3.nullable(),
23655
- targetCountry: z86.string().nullable(),
23656
- languageCode: z86.string().nullable(),
23657
- hasReferenceImage: z86.boolean(),
23658
- hasWinnerReferenceImage: z86.boolean(),
24715
+ targetCountry: z90.string().nullable(),
24716
+ languageCode: z90.string().nullable(),
24717
+ hasReferenceImage: z90.boolean(),
24718
+ hasWinnerReferenceImage: z90.boolean(),
23659
24719
  costingInput: CostingInputSchema.nullable(),
23660
24720
  costingResult: CostingResultSchema.nullable(),
23661
- costingVersion: z86.number().int().positive().nullable(),
23662
- version: z86.number().int().positive(),
24721
+ costingVersion: z90.number().int().positive().nullable(),
24722
+ version: z90.number().int().positive(),
23663
24723
  phase: DropshippingProjectPhase,
23664
24724
  nextAction: DropshippingProjectNextAction.nullable(),
23665
24725
  rewards: RewardProgressSchema,
23666
- createdAt: z86.string().datetime(),
23667
- updatedAt: z86.string().datetime()
24726
+ createdAt: z90.string().datetime(),
24727
+ updatedAt: z90.string().datetime()
23668
24728
  }).passthrough();
23669
24729
  var NEXT_ACTION_GUIDANCE = {
23670
24730
  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.",
@@ -23782,58 +24842,58 @@ function registerDropshippingProjectTools(server, api) {
23782
24842
  __name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
23783
24843
 
23784
24844
  // src/tools/daily-control.ts
23785
- import { z as z87 } from "zod";
24845
+ import { z as z91 } from "zod";
23786
24846
  var getDailyControlGuidance = getProductMakerCapabilityModelGuidance("get_daily_control");
23787
24847
  var linkCampaignToProjectGuidance = getProductMakerCapabilityModelGuidance("link_campaign_to_project");
23788
24848
  var reportDailyReturnsGuidance = getProductMakerCapabilityModelGuidance("report_daily_returns");
23789
- var ProviderCampaignIdentifier = z87.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
23790
- var DateOnly = z87.string().regex(/^\d{4}-\d{2}-\d{2}$/);
23791
- var DailyControlInput = z87.object({
24849
+ var ProviderCampaignIdentifier = z91.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
24850
+ var DateOnly = z91.string().regex(/^\d{4}-\d{2}-\d{2}$/);
24851
+ var DailyControlInput = z91.object({
23792
24852
  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."),
23793
- days: z87.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
23794
- projectId: z87.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
24853
+ days: z91.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
24854
+ projectId: z91.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
23795
24855
  }).strict();
23796
- var LinkCampaignInput = z87.object({
23797
- projectId: z87.string().uuid(),
23798
- provider: z87.enum([
24856
+ var LinkCampaignInput = z91.object({
24857
+ projectId: z91.string().uuid(),
24858
+ provider: z91.enum([
23799
24859
  "meta",
23800
24860
  "tiktok"
23801
24861
  ]),
23802
24862
  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."),
23803
24863
  campaignId: ProviderCampaignIdentifier
23804
24864
  }).strict();
23805
- var ReportReturnsInput = z87.object({
23806
- projectId: z87.string().uuid(),
24865
+ var ReportReturnsInput = z91.object({
24866
+ projectId: z91.string().uuid(),
23807
24867
  date: DateOnly,
23808
- returnedOrders: z87.number().int().min(0).max(1e5),
23809
- note: z87.string().trim().max(280).optional()
24868
+ returnedOrders: z91.number().int().min(0).max(1e5),
24869
+ note: z91.string().trim().max(280).optional()
23810
24870
  }).strict();
23811
- var DailyControlStatusMirror = DailyControlStatusEnum.or(z87.string());
23812
- var DailyControlLightMirror = DailyControlLightEnum.or(z87.string());
23813
- var DailyControlSourceMirror = DailyControlSourceEnum.or(z87.string());
23814
- var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z87.string());
23815
- var DailyControlDayMirror = z87.object({
23816
- date: z87.string(),
23817
- returnedOrders: z87.number(),
24871
+ var DailyControlStatusMirror = DailyControlStatusEnum.or(z91.string());
24872
+ var DailyControlLightMirror = DailyControlLightEnum.or(z91.string());
24873
+ var DailyControlSourceMirror = DailyControlSourceEnum.or(z91.string());
24874
+ var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z91.string());
24875
+ var DailyControlDayMirror = z91.object({
24876
+ date: z91.string(),
24877
+ returnedOrders: z91.number(),
23818
24878
  returnedSource: DailyControlSourceMirror,
23819
24879
  cancelledSource: DailyControlSourceMirror,
23820
- hasEstimates: z87.boolean(),
24880
+ hasEstimates: z91.boolean(),
23821
24881
  light: DailyControlLightMirror
23822
24882
  }).passthrough();
23823
- var DailyControlProductMirror = z87.object({
23824
- projectId: z87.string().uuid(),
23825
- productName: z87.string().nullable(),
24883
+ var DailyControlProductMirror = z91.object({
24884
+ projectId: z91.string().uuid(),
24885
+ productName: z91.string().nullable(),
23826
24886
  status: DailyControlStatusMirror,
23827
- statusMessage: z87.string().nullable(),
23828
- days: z87.array(DailyControlDayMirror),
24887
+ statusMessage: z91.string().nullable(),
24888
+ days: z91.array(DailyControlDayMirror),
23829
24889
  verdict: DailyControlVerdictMirror.nullable(),
23830
- rationale: z87.string().nullable()
24890
+ rationale: z91.string().nullable()
23831
24891
  }).passthrough();
23832
- var DailyControlResponseMirror = z87.object({
23833
- since: z87.string(),
23834
- until: z87.string(),
23835
- products: z87.array(DailyControlProductMirror),
23836
- archivedProjectSpend: z87.array(z87.unknown())
24892
+ var DailyControlResponseMirror = z91.object({
24893
+ since: z91.string(),
24894
+ until: z91.string(),
24895
+ products: z91.array(DailyControlProductMirror),
24896
+ archivedProjectSpend: z91.array(z91.unknown())
23837
24897
  }).passthrough();
23838
24898
  var MCP_WARNINGS_KEY = "_mcpDailyControlWarnings";
23839
24899
  function withWarnings(response) {
@@ -23917,42 +24977,42 @@ function registerDailyControlTools(server, api) {
23917
24977
  __name(registerDailyControlTools, "registerDailyControlTools");
23918
24978
 
23919
24979
  // src/tools/product-control.ts
23920
- import { z as z88 } from "zod";
24980
+ import { z as z92 } from "zod";
23921
24981
  var getProductControlGuidance = getProductMakerCapabilityModelGuidance("get_product_control");
23922
24982
  var listImportableProductsGuidance = getProductMakerCapabilityModelGuidance("list_importable_products");
23923
24983
  var importProductControlsGuidance = getProductMakerCapabilityModelGuidance("import_product_controls");
23924
24984
  var setProductCostingGuidance = getProductMakerCapabilityModelGuidance("set_product_costing");
23925
24985
  var reportProductCountsGuidance = getProductMakerCapabilityModelGuidance("report_product_counts");
23926
- var DateOnly2 = z88.string().regex(/^\d{4}-\d{2}-\d{2}$/);
24986
+ var DateOnly2 = z92.string().regex(/^\d{4}-\d{2}-\d{2}$/);
23927
24987
  var IMPORT_BATCH_MAX2 = 50;
23928
- var ProductControlListInput = z88.object({
23929
- days: z88.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
24988
+ var ProductControlListInput = z92.object({
24989
+ days: z92.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
23930
24990
  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."),
23931
- shopDomain: z88.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
24991
+ shopDomain: z92.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
23932
24992
  }).strict();
23933
- var ImportableProductsInput = z88.object({
23934
- q: z88.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
23935
- after: z88.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
23936
- shopDomain: z88.string().min(1).optional()
24993
+ var ImportableProductsInput = z92.object({
24994
+ q: z92.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
24995
+ after: z92.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
24996
+ shopDomain: z92.string().min(1).optional()
23937
24997
  }).strict();
23938
- var ImportProductControlsInput = z88.object({
23939
- shopifyProductIds: z88.array(z88.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."),
24998
+ var ImportProductControlsInput = z92.object({
24999
+ shopifyProductIds: z92.array(z92.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."),
23940
25000
  // Obligatorio, como en el endpoint: con dos tiendas conectadas, importar
23941
25001
  // sin decir a cuál archivaría el producto bajo la tienda equivocada.
23942
- shopDomain: z88.string().min(1)
25002
+ shopDomain: z92.string().min(1)
23943
25003
  }).strict();
23944
- var SetProductCostingInput = z88.object({
23945
- shopDomain: z88.string().min(1),
23946
- shopifyProductId: z88.string().min(1).max(64),
23947
- targetCountry: z88.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
23948
- supplierCost: z88.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
23949
- shippingCost: z88.number().finite().nonnegative(),
23950
- returnRate: z88.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
23951
- cancellationRate: z88.number().finite().min(0).lt(1)
25004
+ var SetProductCostingInput = z92.object({
25005
+ shopDomain: z92.string().min(1),
25006
+ shopifyProductId: z92.string().min(1).max(64),
25007
+ targetCountry: z92.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
25008
+ supplierCost: z92.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
25009
+ shippingCost: z92.number().finite().nonnegative(),
25010
+ returnRate: z92.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
25011
+ cancellationRate: z92.number().finite().min(0).lt(1)
23952
25012
  }).strict();
23953
- var Count = z88.number().int().min(0).max(1e5).nullable().optional();
23954
- var ReportProductCountsInput = z88.object({
23955
- productControlId: z88.string().uuid().describe("El identificador que devuelve get_product_control."),
25013
+ var Count = z92.number().int().min(0).max(1e5).nullable().optional();
25014
+ var ReportProductCountsInput = z92.object({
25015
+ productControlId: z92.string().uuid().describe("El identificador que devuelve get_product_control."),
23956
25016
  date: DateOnly2,
23957
25017
  orders: Count.describe("Pedidos que entraron ese d\xEDa."),
23958
25018
  cancelledOrders: Count,
@@ -24112,6 +25172,16 @@ function firstHeader(value) {
24112
25172
  }
24113
25173
  __name(firstHeader, "firstHeader");
24114
25174
  var PROXY_FETCH_TIMEOUT_MS = 3e4;
25175
+ var DEFAULT_SUBJECT_TOKEN_REAUTH_WINDOW_MS = 18e4;
25176
+ function subjectTokenReauthWindowMs(env) {
25177
+ const raw = env.MCP_SUBJECT_TOKEN_REAUTH_WINDOW_MS;
25178
+ if (raw === void 0 || raw.trim() === "") {
25179
+ return DEFAULT_SUBJECT_TOKEN_REAUTH_WINDOW_MS;
25180
+ }
25181
+ const parsed = Number.parseInt(raw, 10);
25182
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_SUBJECT_TOKEN_REAUTH_WINDOW_MS;
25183
+ }
25184
+ __name(subjectTokenReauthWindowMs, "subjectTokenReauthWindowMs");
24115
25185
  var INLINE_RENDERABLE_MIMES = /* @__PURE__ */ new Set([
24116
25186
  "image/jpeg",
24117
25187
  "image/png",
@@ -24242,7 +25312,8 @@ function resolveDownstreamAuth(req, opts, oauthAnalytics) {
24242
25312
  credential,
24243
25313
  subject,
24244
25314
  clientId,
24245
- isLegacy: false
25315
+ isLegacy: false,
25316
+ subjectExpiresAt: expiresAt
24246
25317
  };
24247
25318
  }
24248
25319
  __name(resolveDownstreamAuth, "resolveDownstreamAuth");
@@ -24266,6 +25337,14 @@ function createAuthenticatedMcpHandler(opts, oauthAnalytics) {
24266
25337
  return;
24267
25338
  }
24268
25339
  const { credential, subject, clientId, isLegacy } = authContext;
25340
+ const reauthWindowMs = subjectTokenReauthWindowMs(process.env);
25341
+ if (reauthWindowMs > 0 && !isLegacy && typeof authContext.subjectExpiresAt === "number" && authContext.subjectExpiresAt * 1e3 <= Date.now() + reauthWindowMs) {
25342
+ res.status(401).set("WWW-Authenticate", `Bearer error="invalid_token", error_description="Access token expired or expiring soon", scope="${MCP_SCOPE}", resource_metadata="${getOAuthProtectedResourceMetadataUrl2(MCP_RESOURCE)}"`).json({
25343
+ error: "invalid_token",
25344
+ error_description: "Access token expired or expiring soon; refresh or re-authenticate."
25345
+ });
25346
+ return;
25347
+ }
24269
25348
  const abortController = new AbortController();
24270
25349
  const abortRequest = /* @__PURE__ */ __name(() => abortController.abort(), "abortRequest");
24271
25350
  req.once("aborted", abortRequest);