@productmaker/mcp 12.11.0 → 12.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/http.js +199 -110
  2. package/dist/stdio.js +199 -110
  3. package/package.json +1 -1
package/dist/http.js CHANGED
@@ -47,7 +47,11 @@ var NarrativeStyle = z.enum([
47
47
  "animated_storyteller",
48
48
  "animated_inner_journey",
49
49
  "animated_object_soul",
50
- "animated_timeline"
50
+ "animated_timeline",
51
+ // Voiceless story on the unified engine only: on-screen labels, before/after
52
+ // and a format the director picks from the Product Truth. Manual-only and
53
+ // admitted per user (`CREATIVE_VIRAL_NARRATIVE_USER_IDS`); never Auto.
54
+ "viral"
51
55
  ]);
52
56
  var BLOCKED_NARRATIVE_STYLES = [
53
57
  "animated_storyteller",
@@ -93,7 +97,8 @@ var VideoPreset = z.enum([
93
97
  "ugc_listicle_3",
94
98
  "ugc_skeptic_confession",
95
99
  "ugc_demo_review",
96
- "metaphor_product"
100
+ "metaphor_product",
101
+ "viral"
97
102
  ]);
98
103
  var VoiceId = z.string().refine((value) => !value.trim().toLowerCase().startsWith("bank:"), "El banco de voces fue retirado. Usa Auto o la voz privada de un actor.");
99
104
  var _VIDEO_MODEL_SET = new Set(VideoModel.options);
@@ -4531,6 +4536,37 @@ var TaskKind = z8.enum([
4531
4536
  // ../../packages/types/src/product.ts
4532
4537
  import { z as z10 } from "zod";
4533
4538
 
4539
+ // ../../packages/types/src/price-numeric.ts
4540
+ function parsePriceNumeric(raw) {
4541
+ if (!raw) return null;
4542
+ const cleaned = raw.replace(/[^\d.,-]/g, "").trim();
4543
+ if (!cleaned || cleaned === "-") return null;
4544
+ const hasDot = cleaned.includes(".");
4545
+ const hasComma = cleaned.includes(",");
4546
+ let normalized = cleaned;
4547
+ if (hasDot && hasComma) {
4548
+ const lastDot = cleaned.lastIndexOf(".");
4549
+ const lastComma = cleaned.lastIndexOf(",");
4550
+ const decimal = lastDot > lastComma ? "." : ",";
4551
+ const thousands = decimal === "." ? "," : ".";
4552
+ normalized = cleaned.split(thousands).join("");
4553
+ if (decimal === ",") normalized = normalized.replace(",", ".");
4554
+ } else if (hasDot) {
4555
+ const parts = cleaned.split(".");
4556
+ const lastGroup = parts[parts.length - 1] ?? "";
4557
+ const isThousands = parts.length > 2 || parts.length === 2 && lastGroup.length === 3;
4558
+ if (isThousands) normalized = parts.join("");
4559
+ } else if (hasComma) {
4560
+ const parts = cleaned.split(",");
4561
+ const lastGroup = parts[parts.length - 1] ?? "";
4562
+ const isThousands = parts.length > 2 || parts.length === 2 && lastGroup.length === 3;
4563
+ normalized = isThousands ? parts.join("") : cleaned.replace(",", ".");
4564
+ }
4565
+ const n = Number(normalized);
4566
+ return Number.isFinite(n) ? n : null;
4567
+ }
4568
+ __name(parsePriceNumeric, "parsePriceNumeric");
4569
+
4534
4570
  // ../../packages/types/src/product-reference-set.ts
4535
4571
  import { z as z9 } from "zod";
4536
4572
  var ProductReferenceScopeSchema = z9.enum([
@@ -4998,35 +5034,6 @@ var OfferSchema = z10.string().min(1).max(240);
4998
5034
  var CompareAtPriceSchema = z10.string().min(1).max(120);
4999
5035
  var PaymentMethodsSchema = z10.string().min(1).max(240);
5000
5036
  var LanguageCodeSchema = z10.string().regex(/^[a-z]{2}(?:[-_][a-zA-Z]{2})?$/, "Invalid language code").max(10);
5001
- function parsePriceNumeric(raw) {
5002
- if (!raw) return null;
5003
- const cleaned = raw.replace(/[^\d.,-]/g, "").trim();
5004
- if (!cleaned || cleaned === "-") return null;
5005
- const hasDot = cleaned.includes(".");
5006
- const hasComma = cleaned.includes(",");
5007
- let normalized = cleaned;
5008
- if (hasDot && hasComma) {
5009
- const lastDot = cleaned.lastIndexOf(".");
5010
- const lastComma = cleaned.lastIndexOf(",");
5011
- const decimal = lastDot > lastComma ? "." : ",";
5012
- const thousands = decimal === "." ? "," : ".";
5013
- normalized = cleaned.split(thousands).join("");
5014
- if (decimal === ",") normalized = normalized.replace(",", ".");
5015
- } else if (hasDot) {
5016
- const parts = cleaned.split(".");
5017
- const lastGroup = parts[parts.length - 1] ?? "";
5018
- const isThousands = parts.length > 2 || parts.length === 2 && lastGroup.length === 3;
5019
- if (isThousands) normalized = parts.join("");
5020
- } else if (hasComma) {
5021
- const parts = cleaned.split(",");
5022
- const lastGroup = parts[parts.length - 1] ?? "";
5023
- const isThousands = parts.length > 2 || parts.length === 2 && lastGroup.length === 3;
5024
- normalized = isThousands ? parts.join("") : cleaned.replace(",", ".");
5025
- }
5026
- const n = Number(normalized);
5027
- return Number.isFinite(n) ? n : null;
5028
- }
5029
- __name(parsePriceNumeric, "parsePriceNumeric");
5030
5037
  var PRICE_SKIP_SENTINELS = /* @__PURE__ */ new Set([
5031
5038
  "skip",
5032
5039
  "none",
@@ -7861,11 +7868,16 @@ var PersistedBundleBindingSchema = z22.object({
7861
7868
  * the price comparison when it is absent.
7862
7869
  */
7863
7870
  priceAtPublish: z22.string().trim().min(1).max(MAX_MONEY_LENGTH).optional(),
7871
+ compareAtPriceAtPublish: z22.string().trim().min(1).max(MAX_MONEY_LENGTH).nullable().optional(),
7872
+ /** Seller's offer at publish time, independent of what Shopify charged. */
7873
+ offerAtPublish: z22.object({
7874
+ price: z22.string().trim().min(1).max(MAX_MONEY_LENGTH),
7875
+ compareAtPrice: z22.string().trim().min(1).max(MAX_MONEY_LENGTH).nullable()
7876
+ }).strict().optional(),
7864
7877
  /**
7865
7878
  * How the binding was produced: `mapped` = merchant-confirmed against an
7866
7879
  * existing catalog, `created` = ProductMaker-created "Oferta" variant,
7867
- * `implicit` = legacy exact-structure match (not persisted today; kept
7868
- * for forward compatibility).
7880
+ * `implicit` = exact-structure match.
7869
7881
  */
7870
7882
  mode: z22.enum([
7871
7883
  "mapped",
@@ -9396,10 +9408,13 @@ var AnchoredIntel = z25.object({
9396
9408
  * (prod 97352dac), and a blanket upfront preset must NOT block the winner
9397
9409
  * replica (2026-08-04) while a review-time pick does (#1807). The origin is
9398
9410
  * the only thing that tells them apart. Absent ⇒ legacy review decision.
9411
+ * `default` = nobody chose: the server's approval-time market default
9412
+ * (owner A′, 2026-11-07) — a later card pick still replaces it.
9399
9413
  */
9400
9414
  narrativeSelectionSource: z25.enum([
9401
9415
  "create",
9402
- "review"
9416
+ "review",
9417
+ "default"
9403
9418
  ]).optional(),
9404
9419
  painPointIndices: z25.array(z25.number().int().nonnegative()).max(10),
9405
9420
  hookIndices: z25.array(z25.number().int().nonnegative()).max(10),
@@ -11465,6 +11480,28 @@ var CreativeEngineSceneSchema = z38.object({
11465
11480
  timeline: CreativeEngineTimelineSchema.optional(),
11466
11481
  factPaths: z38.array(id2)
11467
11482
  }).strict();
11483
+ var CreativeEngineViralFormatSchema = z38.enum([
11484
+ "pas",
11485
+ "before_after",
11486
+ "demo",
11487
+ "pov",
11488
+ "gift",
11489
+ "concept_vs_result"
11490
+ ]);
11491
+ var CreativeEngineViralPlanSchema = z38.object({
11492
+ format: CreativeEngineViralFormatSchema,
11493
+ /** The scene that shows the product's critical feature working. */
11494
+ mechanismSceneId: id2,
11495
+ /** Two stills of the same subject and framing: rigid surfaces wipe, soft ones (hair, skin, fabric) cut. */
11496
+ beforeAfter: z38.object({
11497
+ beforeSceneId: id2,
11498
+ afterSceneId: id2,
11499
+ surface: z38.enum([
11500
+ "rigid",
11501
+ "soft"
11502
+ ])
11503
+ }).strict().optional()
11504
+ }).strict();
11468
11505
  var CreativeEnginePlanSchema = z38.object({
11469
11506
  revision: z38.number().int().positive(),
11470
11507
  execution: CreativeEngineExecutionSchema.optional(),
@@ -11481,7 +11518,9 @@ var CreativeEnginePlanSchema = z38.object({
11481
11518
  music: z38.literal(false),
11482
11519
  scenes: z38.array(CreativeEngineSceneSchema).min(1).max(CREATIVE_ENGINE_MAX_PLAN_SCENES),
11483
11520
  /** The commercial binding actually applied; absent on guided/historical. */
11484
- commercialExecution: CreativeEngineCommercialExecutionSchema.optional()
11521
+ commercialExecution: CreativeEngineCommercialExecutionSchema.optional(),
11522
+ /** Voiceless «Viral» story; absent on every other narrative. */
11523
+ viral: CreativeEngineViralPlanSchema.optional()
11485
11524
  }).strict();
11486
11525
  var CreativeEngineSpeakerRegionSchema = z38.object({
11487
11526
  x: z38.number().finite().min(0).max(1),
@@ -12001,6 +12040,14 @@ var CreativeEngineStateSchema = z38.object({
12001
12040
  * seller-facing script equals the approved one (no second approval).
12002
12041
  */
12003
12042
  carriedPlanHash: hash.optional(),
12043
+ /**
12044
+ * Who approved without the seller: a «Viral» script has no editable
12045
+ * dialogue, so the system approves it on the seller's behalf.
12046
+ */
12047
+ approvedBy: z38.object({
12048
+ actor: z38.literal("system"),
12049
+ reason: z38.literal("viral_auto")
12050
+ }).strict().optional(),
12004
12051
  waitingStartedAtMs: z38.number().int().nonnegative().optional(),
12005
12052
  pausedMs: z38.number().int().nonnegative()
12006
12053
  }).strict().optional(),
@@ -12225,7 +12272,8 @@ var VideoGeneratorAiEdit = z39.object({
12225
12272
  error: z39.string().optional()
12226
12273
  });
12227
12274
  var VideoCreativeDirection = CreativeEnginePlanSchema.pick({
12228
- guide: true
12275
+ guide: true,
12276
+ viral: true
12229
12277
  });
12230
12278
  var VideoGenerator = z39.object({
12231
12279
  aiEdit: VideoGeneratorAiEdit.nullable().optional(),
@@ -12326,7 +12374,10 @@ var VideoScriptSceneForReview = z39.object({
12326
12374
  /** Objective visual observed in the winner source scene. */
12327
12375
  winnerVisual: z39.string().optional(),
12328
12376
  /** Localized target visual expected to accompany this spoken line. */
12329
- expectedVisual: z39.string().optional()
12377
+ expectedVisual: z39.string().optional(),
12378
+ /** «Viral» (voiceless) plans only: the label burned on screen over this
12379
+ * scene. Shown read-only in place of dialogue; the scene has no voice. */
12380
+ onScreenLabel: z39.string().optional()
12330
12381
  });
12331
12382
  var VIDEO_SCRIPT_APPROVAL_MAX_SCENES = 50;
12332
12383
  var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS = 2e3;
@@ -24226,7 +24277,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24226
24277
  // MCP 8.6.0: + storyteller narrative family and animationStyle axis.
24227
24278
  // MCP 8.10.0: storyteller family blocked (BLOCKED_NARRATIVE_STYLES).
24228
24279
  // MCP 9.1.0: combine current animated-video controls and initial GIF choice.
24229
- "5991b2e897aad7b10c4e38a93e8c58234741498393439cf0228ed9ffd966efef",
24280
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
24281
+ "bfe5130b1941f5f0a690949411d83cdbd32c1ee65cc75f2c22568b87bfe90a42",
24230
24282
  "credit_consuming",
24231
24283
  "creative",
24232
24284
  [
@@ -24285,7 +24337,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24285
24337
  // MCP 9.1.0: combine current animated-video controls and initial GIF choice.
24286
24338
  // MCP 12.4.0: landingStrategy narrowed to classic + social_proof.
24287
24339
  // MCP 12.5.0: pain_solution restored to landingStrategy.
24288
- "68a740d97238e720000c16db56ae5748ceb560e342f8fbfe605c954e6284fda7",
24340
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
24341
+ "bc90378e921718099faae3f6a11cac4f1fdd81727729d6df581f1f827f94a240",
24289
24342
  "credit_consuming",
24290
24343
  "creative",
24291
24344
  [
@@ -24491,28 +24544,38 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24491
24544
  ], [
24492
24545
  "task_draft"
24493
24546
  ]),
24494
- defineCapability("estimate_creative_variant", "468079ba24e5e0d3c5c707cd54fec64571be429e66cadc12019af4d6e9d10c4e", "none", "read_only", [
24495
- "read"
24496
- ], [
24497
- slot("source_task", true, [
24498
- "sourceTaskId"
24499
- ]),
24500
- slot("creative_kind", true, [
24501
- "kind"
24502
- ]),
24503
- slot("variant_options", false, [
24504
- "videoPreset",
24505
- "videoResolution",
24506
- "nVariants",
24507
- "sectionKey",
24508
- "instructions"
24509
- ])
24510
- ], [
24511
- "task",
24512
- "creative"
24513
- ], [
24514
- "cost_estimate"
24515
- ]),
24547
+ defineCapability(
24548
+ "estimate_creative_variant",
24549
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
24550
+ "743d227d050dab27a8b385c9bc2c0c2d5b450e22abfed811c344b65faffb29ab",
24551
+ "none",
24552
+ "read_only",
24553
+ [
24554
+ "read"
24555
+ ],
24556
+ [
24557
+ slot("source_task", true, [
24558
+ "sourceTaskId"
24559
+ ]),
24560
+ slot("creative_kind", true, [
24561
+ "kind"
24562
+ ]),
24563
+ slot("variant_options", false, [
24564
+ "videoPreset",
24565
+ "videoResolution",
24566
+ "nVariants",
24567
+ "sectionKey",
24568
+ "instructions"
24569
+ ])
24570
+ ],
24571
+ [
24572
+ "task",
24573
+ "creative"
24574
+ ],
24575
+ [
24576
+ "cost_estimate"
24577
+ ]
24578
+ ),
24516
24579
  defineCapability("estimate_market_study_cost", "d1fbe8ead642d481258c419a1eed059f9c8188d1cd9f2d4eed12e202fc8800a4", "none", "read_only", [
24517
24580
  "read"
24518
24581
  ], [
@@ -24845,7 +24908,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24845
24908
  // MCP 8.6.0: + storyteller narrative family and animationStyle axis.
24846
24909
  // MCP 8.10.0: storyteller family blocked (BLOCKED_NARRATIVE_STYLES).
24847
24910
  // MCP 9.1.0: combine current animated-video controls and initial GIF choice.
24848
- "66dee35d59da0e88e72e24c503f3f1dc46c17c1162d98d48705e2bfe552f3f02",
24911
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
24912
+ "0669339f0e89332c23588c9a91be31e4700a655367beaa5945569fe329cbf5b9",
24849
24913
  "credit_consuming",
24850
24914
  "creative",
24851
24915
  [
@@ -25624,42 +25688,53 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
25624
25688
  ], [
25625
25689
  "media_upload_session"
25626
25690
  ], MCP_ONLY_CHANNEL_EXPOSURE),
25627
- defineCapability("resolve_pending_action", "a7fef1e3299c2bfb387a16b3e1e243fba8df1db961ebc1f27d2119209124c293", "credit_consuming", "creative", [
25691
+ defineCapability(
25692
+ "resolve_pending_action",
25693
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
25694
+ // MCP 12.12.1: angleEdits[].narrativeSelection documents the server market default.
25695
+ "c15335d63c13e6a086f6b8f8a859bec6345faad6548d7ee637f50103721b5ff0",
25628
25696
  "credit_consuming",
25629
- "state_change"
25630
- ], [
25631
- slot("task", true, [
25632
- "taskId"
25633
- ]),
25634
- slot("action", true, [
25635
- "action"
25636
- ], [
25637
- "actionKind"
25638
- ]),
25639
- slot("action_payload", false, [], [
25640
- "userChoice",
25641
- "selectedAngleIndex",
25642
- "confirmedProductNames",
25643
- "customAngle",
25644
- "painPoints",
25645
- "benefits",
25646
- "features",
25647
- "useMultiSceneShowcase",
25648
- "gapDurationSeconds",
25649
- "narrativeSelection",
25650
- "angleId",
25651
- "content",
25652
- "creativeDirection",
25653
- "actor",
25654
- "voiceovers",
25655
- "visualDirections"
25656
- ])
25657
- ], [
25658
- "task",
25659
- "pending_action"
25660
- ], [
25661
- "task"
25662
- ]),
25697
+ "creative",
25698
+ [
25699
+ "credit_consuming",
25700
+ "state_change"
25701
+ ],
25702
+ [
25703
+ slot("task", true, [
25704
+ "taskId"
25705
+ ]),
25706
+ slot("action", true, [
25707
+ "action"
25708
+ ], [
25709
+ "actionKind"
25710
+ ]),
25711
+ slot("action_payload", false, [], [
25712
+ "userChoice",
25713
+ "selectedAngleIndex",
25714
+ "confirmedProductNames",
25715
+ "customAngle",
25716
+ "painPoints",
25717
+ "benefits",
25718
+ "features",
25719
+ "useMultiSceneShowcase",
25720
+ "gapDurationSeconds",
25721
+ "narrativeSelection",
25722
+ "angleId",
25723
+ "content",
25724
+ "creativeDirection",
25725
+ "actor",
25726
+ "voiceovers",
25727
+ "visualDirections"
25728
+ ])
25729
+ ],
25730
+ [
25731
+ "task",
25732
+ "pending_action"
25733
+ ],
25734
+ [
25735
+ "task"
25736
+ ]
25737
+ ),
25663
25738
  defineCapability("start_dropshipping_project", "996385df905ed85a933c5726fbebf5a29a98f29364badd8256ff306aff38797c", "state_change", "business", [
25664
25739
  "state_change"
25665
25740
  ], [
@@ -26577,7 +26652,10 @@ var MCP_HIDDEN_NARRATIVE_STYLES = [
26577
26652
  "gap_replication",
26578
26653
  // The blocked storyteller family is not offered, not even when revising an
26579
26654
  // existing script.
26580
- ...BLOCKED_NARRATIVE_STYLES
26655
+ ...BLOCKED_NARRATIVE_STYLES,
26656
+ // «Viral» only renders on the unified engine, whose scripts are reviewed in
26657
+ // place: the previous engine's narrative regeneration cannot produce it.
26658
+ "viral"
26581
26659
  ];
26582
26660
  var McpNarrativeStyleSchema = NarrativeStyle.exclude(MCP_HIDDEN_NARRATIVE_STYLES);
26583
26661
  var MCP_ANGLE_NARRATIVE_STYLES = [
@@ -26591,6 +26669,9 @@ var MCP_ANGLE_NARRATIVE_STYLES = [
26591
26669
  "wild_card",
26592
26670
  "animated_problem_pixar",
26593
26671
  "sassy_product_pixar",
26672
+ // Manual-only, voiceless, unified engine only; the API admits it per user
26673
+ // (`CREATIVE_VIRAL_NARRATIVE_USER_IDS`).
26674
+ "viral",
26594
26675
  "market"
26595
26676
  ];
26596
26677
  var McpAngleNarrativeStyleSchema = NarrativeStyle.extract(MCP_ANGLE_NARRATIVE_STYLES);
@@ -27783,8 +27864,11 @@ var CreativeCompositionLocalManifestSchema = z111.object({
27783
27864
  scenes: z111.array(z111.object({
27784
27865
  id: id3,
27785
27866
  startSec: z111.number().nonnegative(),
27786
- durationSec: z111.number().positive()
27787
- }).strict()).min(1)
27867
+ durationSec: z111.number().positive(),
27868
+ /** «Viral» plans only: the one label the page burns over this scene. */
27869
+ copy: z111.array(z111.string()).optional()
27870
+ }).strict()).min(1),
27871
+ viral: CreativeEngineViralPlanSchema.optional()
27788
27872
  }).strict(),
27789
27873
  planHash: hash2,
27790
27874
  context: CreativeEngineContextBindingSchema,
@@ -32212,7 +32296,7 @@ var ApiClient = class {
32212
32296
  };
32213
32297
 
32214
32298
  // src/index.ts
32215
- var MCP_VERSION = "12.11.0";
32299
+ var MCP_VERSION = "12.12.1";
32216
32300
 
32217
32301
  // src/meta-upload-app.ts
32218
32302
  import { readFileSync } from "fs";
@@ -33219,7 +33303,8 @@ var VideoPresetEnum = VideoPreset.extract([
33219
33303
  "cinematic",
33220
33304
  "wild_card",
33221
33305
  "pixar_problem",
33222
- "pixar_sassy"
33306
+ "pixar_sassy",
33307
+ "viral"
33223
33308
  ]);
33224
33309
  var VideoStructureEnum = VideoStructure;
33225
33310
  var VideoQualityEnum = VideoQuality;
@@ -33251,6 +33336,10 @@ var VIDEO_PRESET_RESOLUTION = {
33251
33336
  videoStyle: "ai_dynamic",
33252
33337
  narrativeStyle: "sassy_product_pixar"
33253
33338
  },
33339
+ viral: {
33340
+ videoStyle: "ai_dynamic",
33341
+ narrativeStyle: "viral"
33342
+ },
33254
33343
  product_showcase: {
33255
33344
  videoStyle: "product_only",
33256
33345
  narrativeStyle: "product_showcase"
@@ -36133,7 +36222,7 @@ var CreateProductTaskInput = z141.object({
36133
36222
  paymentMethods: z141.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.'),
36134
36223
  productContext: z141.string().max(PRODUCT_CONTEXT_MAX_CHARS).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."),
36135
36224
  productLabel: z141.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.'),
36136
- 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); "problem_solution" en single_call abre sin el producto y el producto entra por referencia en su beat de soluci\xF3n. 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.'),
36225
+ 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 salvo "viral" (solo Multiescena de 15 s) funcionan a 15s (single_call) y 30s (Multiescena); "problem_solution" en single_call abre sin el producto y el producto entra por referencia en su beat de soluci\xF3n. 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), "viral" (video sin voz con r\xF3tulos en pantalla y antes/despu\xE9s; solo Multiescena de 15 s y solo para cuentas habilitadas; la API rechaza otras combinaciones). Si el usuario pide algo fuera de esta lista, p\xEDdele una opci\xF3n v\xE1lida.'),
36137
36226
  animationStyle: AnimationStyleEnum.optional().describe('Opcional. Estilo visual SOLO para presets animados ("pixar_problem" o "pixar_sassy") \u2014 la API lo rechaza con cualquier otro preset. Opciones: "pixar_3d" (default, look 3D de pel\xEDcula animada), "claymation" (plastilina stop-motion artesanal), "wes_anderson" (tableau sim\xE9trico pastel), "sci_fi" (futurista con HUDs y ne\xF3n), "realista" (fotorrealista, fiel al producto). Si el usuario pide un look animado sin nombrar el estilo, omite el campo.'),
36138
36227
  durationSeconds: z141.union([
36139
36228
  z141.literal(15),
@@ -36146,7 +36235,7 @@ var CreateProductTaskInput = z141.object({
36146
36235
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
36147
36236
  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).'),
36148
36237
  videoAngleCount: z141.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."),
36149
- 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.'),
36238
+ 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", "viral" (video sin voz: r\xF3tulos en pantalla y antes/despu\xE9s; solo Multiescena de 15 s y solo para cuentas habilitadas; la API rechaza otras combinaciones), "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.'),
36150
36239
  outputs: z141.array(z141.enum([
36151
36240
  "landing",
36152
36241
  "video",
@@ -36256,7 +36345,7 @@ var PendingActionPayload = z141.discriminatedUnion("kind", [
36256
36345
  // an opaque HTTP 400 after the model already believed it had been saved.
36257
36346
  features: z141.array(z141.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
36258
36347
  buyerPersona: z141.string().min(1).max(360).optional(),
36259
- narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora.'),
36348
+ narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora. "viral" (sin voz, r\xF3tulos y antes/despu\xE9s; solo Multiescena de 15 s) solo est\xE1 disponible para cuentas habilitadas y tareas del motor nuevo.'),
36260
36349
  actor: z141.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."),
36261
36350
  actorId: z141.string().max(120).nullable().optional(),
36262
36351
  creativeDirection: z141.string().max(2e3).optional(),
@@ -36266,10 +36355,10 @@ var PendingActionPayload = z141.discriminatedUnion("kind", [
36266
36355
  narrativeSelection: z141.enum([
36267
36356
  "market",
36268
36357
  "guided"
36269
- ]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo."),
36358
+ ]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo. Si la omites y la tarea tiene un estudio de mercado listo, el \xE1ngulo sale en market; repetir la narrativeStyle que propuso la IA no cuenta como elecci\xF3n."),
36270
36359
  angle: z141.string().min(1).max(480).optional(),
36271
36360
  buyerPersona: z141.string().min(1).max(360).optional(),
36272
- narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora.'),
36361
+ narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora. "viral" (sin voz, r\xF3tulos y antes/despu\xE9s; solo Multiescena de 15 s) solo est\xE1 disponible para cuentas habilitadas y tareas del motor nuevo.'),
36273
36362
  actor: z141.string().min(1).max(360).optional(),
36274
36363
  actorId: z141.string().max(120).nullable().optional(),
36275
36364
  creativeDirection: z141.string().max(2e3).optional()
@@ -38429,7 +38518,7 @@ var VideoCreativeSetup = z145.discriminatedUnion("mode", [
38429
38518
  }).strict(),
38430
38519
  z145.object({
38431
38520
  mode: z145.literal("guided"),
38432
- 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. Todas las narrativas funcionan en Un solo clip y Multiescena.'),
38521
+ 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. Todas las narrativas salvo "viral" funcionan en Un solo clip y Multiescena. "viral" es un video sin voz (r\xF3tulos en pantalla, antes/despu\xE9s), solo Multiescena de 15 s y solo para cuentas habilitadas; la API rechaza otras combinaciones.'),
38433
38522
  buyerPersona: z145.string().trim().min(1).max(360),
38434
38523
  animationStyle: AnimationStyleEnum.optional().describe('Opcional. Estilo visual SOLO para presets animados ("pixar_problem" o "pixar_sassy") \u2014 la API lo rechaza con cualquier otro preset. Opciones: "pixar_3d" (default, look 3D de pel\xEDcula animada), "claymation" (plastilina stop-motion artesanal), "wes_anderson" (tableau sim\xE9trico pastel), "sci_fi" (futurista con HUDs y ne\xF3n), "realista" (fotorrealista, fiel al producto). Si el usuario pide un look animado sin nombrar el estilo, omite el campo.'),
38435
38524
  presenter: VideoPresenter,
@@ -38451,7 +38540,7 @@ var VideoInput = z145.object({
38451
38540
  generateAiEdit: AiEditRequestSchema.optional().describe('Opcional. Pide la Edici\xF3n con IA: una pasada que a\xF1ade texto, efectos y m\xFAsica sobre cada video entregado (variante with_ai_edit; se cobra al entregar, por video). Nivel: "economy" (Econ\xF3mica, Sonnet 5.5, m\xE1s barata) o "pro" (Opus 5.5, mejor calidad, ~10 min). true equivale a "pro"; false/omitido = sin edici\xF3n.'),
38452
38541
  durationSeconds: z145.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."),
38453
38542
  videoAngleCount: z145.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."),
38454
- 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.'),
38543
+ 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", "viral" (video sin voz: r\xF3tulos en pantalla y antes/despu\xE9s; solo Multiescena de 15 s y solo para cuentas habilitadas; la API rechaza otras combinaciones), "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.'),
38455
38544
  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."),
38456
38545
  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.'),
38457
38546
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
@@ -39364,7 +39453,7 @@ var RegenerateVideoScriptInput = RegenerateVideoScriptCapabilityRequest.extend({
39364
39453
  });
39365
39454
  function registerScriptReviewTools(server, api) {
39366
39455
  server.registerTool("get_video_script", {
39367
- description: getVideoScriptGuidance.summary + ' S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId?, persuasiveFunction?, winnerVisual?, expectedVisual? }], error? }. En escenas copiadas de un winner, `persuasiveFunction` explica qu\xE9 logra ese beat, `winnerVisual` conserva la observaci\xF3n fuente y `expectedVisual` describe nuestra adaptaci\xF3n localizada; muestra los tres junto al di\xE1logo para que una edici\xF3n no desaline\xE9 guion y visual. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado; mu\xE9straselo al usuario.\n\nREGLA DURA: status="ready" es un estado terminal de interacci\xF3n. Presenta el guion una sola vez y NO vuelvas a llamar get_video_script ni get_task_status hasta que el usuario responda aprobar, editar o regenerar. La respuesta a\xF1ade interactionState="awaiting_user_review" y nextAction="present_script_and_wait".\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y llama este tool; su campo `status` es la se\xF1al real:\n- "ready" \u2192 presenta todas las escenas y DETENTE hasta la respuesta del usuario.\n- "not_ready" \u2192 espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle consulta otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar.\n- "error" \u2192 consulta `get_task_status` antes de ofrecer recuperaci\xF3n. No regeneres autom\xE1ticamente una tarea terminal.\n\nANGLE KEY: usa las claves expl\xEDcitas de reviewNextAction.calls, incluso si s\xF3lo seleccionaste un candidato. Cuando uno est\xE9 ready, detente en ese \xE1ngulo y espera la decisi\xF3n antes de consultar otro.',
39456
+ description: getVideoScriptGuidance.summary + ' S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId?, persuasiveFunction?, winnerVisual?, expectedVisual?, onScreenLabel? }], error? }. En videos \xABViral\xBB (sin voz) cada escena trae `onScreenLabel`: mu\xE9stralo como \xABTexto en pantalla\xBB; su `dialogue` viene vac\xEDo y no pidas di\xE1logo para esas escenas. En escenas copiadas de un winner, `persuasiveFunction` explica qu\xE9 logra ese beat, `winnerVisual` conserva la observaci\xF3n fuente y `expectedVisual` describe nuestra adaptaci\xF3n localizada; muestra los tres junto al di\xE1logo para que una edici\xF3n no desaline\xE9 guion y visual. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado; mu\xE9straselo al usuario.\n\nREGLA DURA: status="ready" es un estado terminal de interacci\xF3n. Presenta el guion una sola vez y NO vuelvas a llamar get_video_script ni get_task_status hasta que el usuario responda aprobar, editar o regenerar. La respuesta a\xF1ade interactionState="awaiting_user_review" y nextAction="present_script_and_wait".\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y llama este tool; su campo `status` es la se\xF1al real:\n- "ready" \u2192 presenta todas las escenas y DETENTE hasta la respuesta del usuario.\n- "not_ready" \u2192 espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle consulta otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar.\n- "error" \u2192 consulta `get_task_status` antes de ofrecer recuperaci\xF3n. No regeneres autom\xE1ticamente una tarea terminal.\n\nANGLE KEY: usa las claves expl\xEDcitas de reviewNextAction.calls, incluso si s\xF3lo seleccionaste un candidato. Cuando uno est\xE9 ready, detente en ese \xE1ngulo y espera la decisi\xF3n antes de consultar otro.',
39368
39457
  inputSchema: GetVideoScriptInput.shape,
39369
39458
  annotations: READS_INTERNAL
39370
39459
  }, async (input) => {
package/dist/stdio.js CHANGED
@@ -69,7 +69,11 @@ var NarrativeStyle = z.enum([
69
69
  "animated_storyteller",
70
70
  "animated_inner_journey",
71
71
  "animated_object_soul",
72
- "animated_timeline"
72
+ "animated_timeline",
73
+ // Voiceless story on the unified engine only: on-screen labels, before/after
74
+ // and a format the director picks from the Product Truth. Manual-only and
75
+ // admitted per user (`CREATIVE_VIRAL_NARRATIVE_USER_IDS`); never Auto.
76
+ "viral"
73
77
  ]);
74
78
  var BLOCKED_NARRATIVE_STYLES = [
75
79
  "animated_storyteller",
@@ -115,7 +119,8 @@ var VideoPreset = z.enum([
115
119
  "ugc_listicle_3",
116
120
  "ugc_skeptic_confession",
117
121
  "ugc_demo_review",
118
- "metaphor_product"
122
+ "metaphor_product",
123
+ "viral"
119
124
  ]);
120
125
  var VoiceId = z.string().refine((value) => !value.trim().toLowerCase().startsWith("bank:"), "El banco de voces fue retirado. Usa Auto o la voz privada de un actor.");
121
126
  var _VIDEO_MODEL_SET = new Set(VideoModel.options);
@@ -4306,6 +4311,37 @@ var TaskKind = z8.enum([
4306
4311
  // ../../packages/types/src/product.ts
4307
4312
  import { z as z10 } from "zod";
4308
4313
 
4314
+ // ../../packages/types/src/price-numeric.ts
4315
+ function parsePriceNumeric(raw) {
4316
+ if (!raw) return null;
4317
+ const cleaned = raw.replace(/[^\d.,-]/g, "").trim();
4318
+ if (!cleaned || cleaned === "-") return null;
4319
+ const hasDot = cleaned.includes(".");
4320
+ const hasComma = cleaned.includes(",");
4321
+ let normalized = cleaned;
4322
+ if (hasDot && hasComma) {
4323
+ const lastDot = cleaned.lastIndexOf(".");
4324
+ const lastComma = cleaned.lastIndexOf(",");
4325
+ const decimal = lastDot > lastComma ? "." : ",";
4326
+ const thousands = decimal === "." ? "," : ".";
4327
+ normalized = cleaned.split(thousands).join("");
4328
+ if (decimal === ",") normalized = normalized.replace(",", ".");
4329
+ } else if (hasDot) {
4330
+ const parts = cleaned.split(".");
4331
+ const lastGroup = parts[parts.length - 1] ?? "";
4332
+ const isThousands = parts.length > 2 || parts.length === 2 && lastGroup.length === 3;
4333
+ if (isThousands) normalized = parts.join("");
4334
+ } else if (hasComma) {
4335
+ const parts = cleaned.split(",");
4336
+ const lastGroup = parts[parts.length - 1] ?? "";
4337
+ const isThousands = parts.length > 2 || parts.length === 2 && lastGroup.length === 3;
4338
+ normalized = isThousands ? parts.join("") : cleaned.replace(",", ".");
4339
+ }
4340
+ const n = Number(normalized);
4341
+ return Number.isFinite(n) ? n : null;
4342
+ }
4343
+ __name(parsePriceNumeric, "parsePriceNumeric");
4344
+
4309
4345
  // ../../packages/types/src/product-reference-set.ts
4310
4346
  import { z as z9 } from "zod";
4311
4347
  var ProductReferenceScopeSchema = z9.enum([
@@ -4773,35 +4809,6 @@ var OfferSchema = z10.string().min(1).max(240);
4773
4809
  var CompareAtPriceSchema = z10.string().min(1).max(120);
4774
4810
  var PaymentMethodsSchema = z10.string().min(1).max(240);
4775
4811
  var LanguageCodeSchema = z10.string().regex(/^[a-z]{2}(?:[-_][a-zA-Z]{2})?$/, "Invalid language code").max(10);
4776
- function parsePriceNumeric(raw) {
4777
- if (!raw) return null;
4778
- const cleaned = raw.replace(/[^\d.,-]/g, "").trim();
4779
- if (!cleaned || cleaned === "-") return null;
4780
- const hasDot = cleaned.includes(".");
4781
- const hasComma = cleaned.includes(",");
4782
- let normalized = cleaned;
4783
- if (hasDot && hasComma) {
4784
- const lastDot = cleaned.lastIndexOf(".");
4785
- const lastComma = cleaned.lastIndexOf(",");
4786
- const decimal = lastDot > lastComma ? "." : ",";
4787
- const thousands = decimal === "." ? "," : ".";
4788
- normalized = cleaned.split(thousands).join("");
4789
- if (decimal === ",") normalized = normalized.replace(",", ".");
4790
- } else if (hasDot) {
4791
- const parts = cleaned.split(".");
4792
- const lastGroup = parts[parts.length - 1] ?? "";
4793
- const isThousands = parts.length > 2 || parts.length === 2 && lastGroup.length === 3;
4794
- if (isThousands) normalized = parts.join("");
4795
- } else if (hasComma) {
4796
- const parts = cleaned.split(",");
4797
- const lastGroup = parts[parts.length - 1] ?? "";
4798
- const isThousands = parts.length > 2 || parts.length === 2 && lastGroup.length === 3;
4799
- normalized = isThousands ? parts.join("") : cleaned.replace(",", ".");
4800
- }
4801
- const n = Number(normalized);
4802
- return Number.isFinite(n) ? n : null;
4803
- }
4804
- __name(parsePriceNumeric, "parsePriceNumeric");
4805
4812
  var PRICE_SKIP_SENTINELS = /* @__PURE__ */ new Set([
4806
4813
  "skip",
4807
4814
  "none",
@@ -7636,11 +7643,16 @@ var PersistedBundleBindingSchema = z22.object({
7636
7643
  * the price comparison when it is absent.
7637
7644
  */
7638
7645
  priceAtPublish: z22.string().trim().min(1).max(MAX_MONEY_LENGTH).optional(),
7646
+ compareAtPriceAtPublish: z22.string().trim().min(1).max(MAX_MONEY_LENGTH).nullable().optional(),
7647
+ /** Seller's offer at publish time, independent of what Shopify charged. */
7648
+ offerAtPublish: z22.object({
7649
+ price: z22.string().trim().min(1).max(MAX_MONEY_LENGTH),
7650
+ compareAtPrice: z22.string().trim().min(1).max(MAX_MONEY_LENGTH).nullable()
7651
+ }).strict().optional(),
7639
7652
  /**
7640
7653
  * How the binding was produced: `mapped` = merchant-confirmed against an
7641
7654
  * existing catalog, `created` = ProductMaker-created "Oferta" variant,
7642
- * `implicit` = legacy exact-structure match (not persisted today; kept
7643
- * for forward compatibility).
7655
+ * `implicit` = exact-structure match.
7644
7656
  */
7645
7657
  mode: z22.enum([
7646
7658
  "mapped",
@@ -9171,10 +9183,13 @@ var AnchoredIntel = z25.object({
9171
9183
  * (prod 97352dac), and a blanket upfront preset must NOT block the winner
9172
9184
  * replica (2026-08-04) while a review-time pick does (#1807). The origin is
9173
9185
  * the only thing that tells them apart. Absent ⇒ legacy review decision.
9186
+ * `default` = nobody chose: the server's approval-time market default
9187
+ * (owner A′, 2026-11-07) — a later card pick still replaces it.
9174
9188
  */
9175
9189
  narrativeSelectionSource: z25.enum([
9176
9190
  "create",
9177
- "review"
9191
+ "review",
9192
+ "default"
9178
9193
  ]).optional(),
9179
9194
  painPointIndices: z25.array(z25.number().int().nonnegative()).max(10),
9180
9195
  hookIndices: z25.array(z25.number().int().nonnegative()).max(10),
@@ -11240,6 +11255,28 @@ var CreativeEngineSceneSchema = z38.object({
11240
11255
  timeline: CreativeEngineTimelineSchema.optional(),
11241
11256
  factPaths: z38.array(id2)
11242
11257
  }).strict();
11258
+ var CreativeEngineViralFormatSchema = z38.enum([
11259
+ "pas",
11260
+ "before_after",
11261
+ "demo",
11262
+ "pov",
11263
+ "gift",
11264
+ "concept_vs_result"
11265
+ ]);
11266
+ var CreativeEngineViralPlanSchema = z38.object({
11267
+ format: CreativeEngineViralFormatSchema,
11268
+ /** The scene that shows the product's critical feature working. */
11269
+ mechanismSceneId: id2,
11270
+ /** Two stills of the same subject and framing: rigid surfaces wipe, soft ones (hair, skin, fabric) cut. */
11271
+ beforeAfter: z38.object({
11272
+ beforeSceneId: id2,
11273
+ afterSceneId: id2,
11274
+ surface: z38.enum([
11275
+ "rigid",
11276
+ "soft"
11277
+ ])
11278
+ }).strict().optional()
11279
+ }).strict();
11243
11280
  var CreativeEnginePlanSchema = z38.object({
11244
11281
  revision: z38.number().int().positive(),
11245
11282
  execution: CreativeEngineExecutionSchema.optional(),
@@ -11256,7 +11293,9 @@ var CreativeEnginePlanSchema = z38.object({
11256
11293
  music: z38.literal(false),
11257
11294
  scenes: z38.array(CreativeEngineSceneSchema).min(1).max(CREATIVE_ENGINE_MAX_PLAN_SCENES),
11258
11295
  /** The commercial binding actually applied; absent on guided/historical. */
11259
- commercialExecution: CreativeEngineCommercialExecutionSchema.optional()
11296
+ commercialExecution: CreativeEngineCommercialExecutionSchema.optional(),
11297
+ /** Voiceless «Viral» story; absent on every other narrative. */
11298
+ viral: CreativeEngineViralPlanSchema.optional()
11260
11299
  }).strict();
11261
11300
  var CreativeEngineSpeakerRegionSchema = z38.object({
11262
11301
  x: z38.number().finite().min(0).max(1),
@@ -11776,6 +11815,14 @@ var CreativeEngineStateSchema = z38.object({
11776
11815
  * seller-facing script equals the approved one (no second approval).
11777
11816
  */
11778
11817
  carriedPlanHash: hash.optional(),
11818
+ /**
11819
+ * Who approved without the seller: a «Viral» script has no editable
11820
+ * dialogue, so the system approves it on the seller's behalf.
11821
+ */
11822
+ approvedBy: z38.object({
11823
+ actor: z38.literal("system"),
11824
+ reason: z38.literal("viral_auto")
11825
+ }).strict().optional(),
11779
11826
  waitingStartedAtMs: z38.number().int().nonnegative().optional(),
11780
11827
  pausedMs: z38.number().int().nonnegative()
11781
11828
  }).strict().optional(),
@@ -12000,7 +12047,8 @@ var VideoGeneratorAiEdit = z39.object({
12000
12047
  error: z39.string().optional()
12001
12048
  });
12002
12049
  var VideoCreativeDirection = CreativeEnginePlanSchema.pick({
12003
- guide: true
12050
+ guide: true,
12051
+ viral: true
12004
12052
  });
12005
12053
  var VideoGenerator = z39.object({
12006
12054
  aiEdit: VideoGeneratorAiEdit.nullable().optional(),
@@ -12101,7 +12149,10 @@ var VideoScriptSceneForReview = z39.object({
12101
12149
  /** Objective visual observed in the winner source scene. */
12102
12150
  winnerVisual: z39.string().optional(),
12103
12151
  /** Localized target visual expected to accompany this spoken line. */
12104
- expectedVisual: z39.string().optional()
12152
+ expectedVisual: z39.string().optional(),
12153
+ /** «Viral» (voiceless) plans only: the label burned on screen over this
12154
+ * scene. Shown read-only in place of dialogue; the scene has no voice. */
12155
+ onScreenLabel: z39.string().optional()
12105
12156
  });
12106
12157
  var VIDEO_SCRIPT_APPROVAL_MAX_SCENES = 50;
12107
12158
  var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS = 2e3;
@@ -24001,7 +24052,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24001
24052
  // MCP 8.6.0: + storyteller narrative family and animationStyle axis.
24002
24053
  // MCP 8.10.0: storyteller family blocked (BLOCKED_NARRATIVE_STYLES).
24003
24054
  // MCP 9.1.0: combine current animated-video controls and initial GIF choice.
24004
- "5991b2e897aad7b10c4e38a93e8c58234741498393439cf0228ed9ffd966efef",
24055
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
24056
+ "bfe5130b1941f5f0a690949411d83cdbd32c1ee65cc75f2c22568b87bfe90a42",
24005
24057
  "credit_consuming",
24006
24058
  "creative",
24007
24059
  [
@@ -24060,7 +24112,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24060
24112
  // MCP 9.1.0: combine current animated-video controls and initial GIF choice.
24061
24113
  // MCP 12.4.0: landingStrategy narrowed to classic + social_proof.
24062
24114
  // MCP 12.5.0: pain_solution restored to landingStrategy.
24063
- "68a740d97238e720000c16db56ae5748ceb560e342f8fbfe605c954e6284fda7",
24115
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
24116
+ "bc90378e921718099faae3f6a11cac4f1fdd81727729d6df581f1f827f94a240",
24064
24117
  "credit_consuming",
24065
24118
  "creative",
24066
24119
  [
@@ -24266,28 +24319,38 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24266
24319
  ], [
24267
24320
  "task_draft"
24268
24321
  ]),
24269
- defineCapability("estimate_creative_variant", "468079ba24e5e0d3c5c707cd54fec64571be429e66cadc12019af4d6e9d10c4e", "none", "read_only", [
24270
- "read"
24271
- ], [
24272
- slot("source_task", true, [
24273
- "sourceTaskId"
24274
- ]),
24275
- slot("creative_kind", true, [
24276
- "kind"
24277
- ]),
24278
- slot("variant_options", false, [
24279
- "videoPreset",
24280
- "videoResolution",
24281
- "nVariants",
24282
- "sectionKey",
24283
- "instructions"
24284
- ])
24285
- ], [
24286
- "task",
24287
- "creative"
24288
- ], [
24289
- "cost_estimate"
24290
- ]),
24322
+ defineCapability(
24323
+ "estimate_creative_variant",
24324
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
24325
+ "743d227d050dab27a8b385c9bc2c0c2d5b450e22abfed811c344b65faffb29ab",
24326
+ "none",
24327
+ "read_only",
24328
+ [
24329
+ "read"
24330
+ ],
24331
+ [
24332
+ slot("source_task", true, [
24333
+ "sourceTaskId"
24334
+ ]),
24335
+ slot("creative_kind", true, [
24336
+ "kind"
24337
+ ]),
24338
+ slot("variant_options", false, [
24339
+ "videoPreset",
24340
+ "videoResolution",
24341
+ "nVariants",
24342
+ "sectionKey",
24343
+ "instructions"
24344
+ ])
24345
+ ],
24346
+ [
24347
+ "task",
24348
+ "creative"
24349
+ ],
24350
+ [
24351
+ "cost_estimate"
24352
+ ]
24353
+ ),
24291
24354
  defineCapability("estimate_market_study_cost", "d1fbe8ead642d481258c419a1eed059f9c8188d1cd9f2d4eed12e202fc8800a4", "none", "read_only", [
24292
24355
  "read"
24293
24356
  ], [
@@ -24620,7 +24683,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24620
24683
  // MCP 8.6.0: + storyteller narrative family and animationStyle axis.
24621
24684
  // MCP 8.10.0: storyteller family blocked (BLOCKED_NARRATIVE_STYLES).
24622
24685
  // MCP 9.1.0: combine current animated-video controls and initial GIF choice.
24623
- "66dee35d59da0e88e72e24c503f3f1dc46c17c1162d98d48705e2bfe552f3f02",
24686
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
24687
+ "0669339f0e89332c23588c9a91be31e4700a655367beaa5945569fe329cbf5b9",
24624
24688
  "credit_consuming",
24625
24689
  "creative",
24626
24690
  [
@@ -25399,42 +25463,53 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
25399
25463
  ], [
25400
25464
  "media_upload_session"
25401
25465
  ], MCP_ONLY_CHANNEL_EXPOSURE),
25402
- defineCapability("resolve_pending_action", "a7fef1e3299c2bfb387a16b3e1e243fba8df1db961ebc1f27d2119209124c293", "credit_consuming", "creative", [
25466
+ defineCapability(
25467
+ "resolve_pending_action",
25468
+ // MCP 12.8.0: + manual-only, cohort-gated «viral» preset/angle narrative.
25469
+ // MCP 12.12.1: angleEdits[].narrativeSelection documents the server market default.
25470
+ "c15335d63c13e6a086f6b8f8a859bec6345faad6548d7ee637f50103721b5ff0",
25403
25471
  "credit_consuming",
25404
- "state_change"
25405
- ], [
25406
- slot("task", true, [
25407
- "taskId"
25408
- ]),
25409
- slot("action", true, [
25410
- "action"
25411
- ], [
25412
- "actionKind"
25413
- ]),
25414
- slot("action_payload", false, [], [
25415
- "userChoice",
25416
- "selectedAngleIndex",
25417
- "confirmedProductNames",
25418
- "customAngle",
25419
- "painPoints",
25420
- "benefits",
25421
- "features",
25422
- "useMultiSceneShowcase",
25423
- "gapDurationSeconds",
25424
- "narrativeSelection",
25425
- "angleId",
25426
- "content",
25427
- "creativeDirection",
25428
- "actor",
25429
- "voiceovers",
25430
- "visualDirections"
25431
- ])
25432
- ], [
25433
- "task",
25434
- "pending_action"
25435
- ], [
25436
- "task"
25437
- ]),
25472
+ "creative",
25473
+ [
25474
+ "credit_consuming",
25475
+ "state_change"
25476
+ ],
25477
+ [
25478
+ slot("task", true, [
25479
+ "taskId"
25480
+ ]),
25481
+ slot("action", true, [
25482
+ "action"
25483
+ ], [
25484
+ "actionKind"
25485
+ ]),
25486
+ slot("action_payload", false, [], [
25487
+ "userChoice",
25488
+ "selectedAngleIndex",
25489
+ "confirmedProductNames",
25490
+ "customAngle",
25491
+ "painPoints",
25492
+ "benefits",
25493
+ "features",
25494
+ "useMultiSceneShowcase",
25495
+ "gapDurationSeconds",
25496
+ "narrativeSelection",
25497
+ "angleId",
25498
+ "content",
25499
+ "creativeDirection",
25500
+ "actor",
25501
+ "voiceovers",
25502
+ "visualDirections"
25503
+ ])
25504
+ ],
25505
+ [
25506
+ "task",
25507
+ "pending_action"
25508
+ ],
25509
+ [
25510
+ "task"
25511
+ ]
25512
+ ),
25438
25513
  defineCapability("start_dropshipping_project", "996385df905ed85a933c5726fbebf5a29a98f29364badd8256ff306aff38797c", "state_change", "business", [
25439
25514
  "state_change"
25440
25515
  ], [
@@ -26345,7 +26420,10 @@ var MCP_HIDDEN_NARRATIVE_STYLES = [
26345
26420
  "gap_replication",
26346
26421
  // The blocked storyteller family is not offered, not even when revising an
26347
26422
  // existing script.
26348
- ...BLOCKED_NARRATIVE_STYLES
26423
+ ...BLOCKED_NARRATIVE_STYLES,
26424
+ // «Viral» only renders on the unified engine, whose scripts are reviewed in
26425
+ // place: the previous engine's narrative regeneration cannot produce it.
26426
+ "viral"
26349
26427
  ];
26350
26428
  var McpNarrativeStyleSchema = NarrativeStyle.exclude(MCP_HIDDEN_NARRATIVE_STYLES);
26351
26429
  var MCP_ANGLE_NARRATIVE_STYLES = [
@@ -26359,6 +26437,9 @@ var MCP_ANGLE_NARRATIVE_STYLES = [
26359
26437
  "wild_card",
26360
26438
  "animated_problem_pixar",
26361
26439
  "sassy_product_pixar",
26440
+ // Manual-only, voiceless, unified engine only; the API admits it per user
26441
+ // (`CREATIVE_VIRAL_NARRATIVE_USER_IDS`).
26442
+ "viral",
26362
26443
  "market"
26363
26444
  ];
26364
26445
  var McpAngleNarrativeStyleSchema = NarrativeStyle.extract(MCP_ANGLE_NARRATIVE_STYLES);
@@ -27551,8 +27632,11 @@ var CreativeCompositionLocalManifestSchema = z111.object({
27551
27632
  scenes: z111.array(z111.object({
27552
27633
  id: id3,
27553
27634
  startSec: z111.number().nonnegative(),
27554
- durationSec: z111.number().positive()
27555
- }).strict()).min(1)
27635
+ durationSec: z111.number().positive(),
27636
+ /** «Viral» plans only: the one label the page burns over this scene. */
27637
+ copy: z111.array(z111.string()).optional()
27638
+ }).strict()).min(1),
27639
+ viral: CreativeEngineViralPlanSchema.optional()
27556
27640
  }).strict(),
27557
27641
  planHash: hash2,
27558
27642
  context: CreativeEngineContextBindingSchema,
@@ -30539,7 +30623,7 @@ var ApiClient = class {
30539
30623
  };
30540
30624
 
30541
30625
  // src/index.ts
30542
- var MCP_VERSION = "12.11.0";
30626
+ var MCP_VERSION = "12.12.1";
30543
30627
 
30544
30628
  // src/meta-upload-app.ts
30545
30629
  import { readFileSync } from "fs";
@@ -31550,7 +31634,8 @@ var VideoPresetEnum = VideoPreset.extract([
31550
31634
  "cinematic",
31551
31635
  "wild_card",
31552
31636
  "pixar_problem",
31553
- "pixar_sassy"
31637
+ "pixar_sassy",
31638
+ "viral"
31554
31639
  ]);
31555
31640
  var VideoStructureEnum = VideoStructure;
31556
31641
  var VideoQualityEnum = VideoQuality;
@@ -31582,6 +31667,10 @@ var VIDEO_PRESET_RESOLUTION = {
31582
31667
  videoStyle: "ai_dynamic",
31583
31668
  narrativeStyle: "sassy_product_pixar"
31584
31669
  },
31670
+ viral: {
31671
+ videoStyle: "ai_dynamic",
31672
+ narrativeStyle: "viral"
31673
+ },
31585
31674
  product_showcase: {
31586
31675
  videoStyle: "product_only",
31587
31676
  narrativeStyle: "product_showcase"
@@ -34618,7 +34707,7 @@ var CreateProductTaskInput = z140.object({
34618
34707
  paymentMethods: z140.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.'),
34619
34708
  productContext: z140.string().max(PRODUCT_CONTEXT_MAX_CHARS).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."),
34620
34709
  productLabel: z140.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.'),
34621
- 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); "problem_solution" en single_call abre sin el producto y el producto entra por referencia en su beat de soluci\xF3n. 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.'),
34710
+ 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 salvo "viral" (solo Multiescena de 15 s) funcionan a 15s (single_call) y 30s (Multiescena); "problem_solution" en single_call abre sin el producto y el producto entra por referencia en su beat de soluci\xF3n. 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), "viral" (video sin voz con r\xF3tulos en pantalla y antes/despu\xE9s; solo Multiescena de 15 s y solo para cuentas habilitadas; la API rechaza otras combinaciones). Si el usuario pide algo fuera de esta lista, p\xEDdele una opci\xF3n v\xE1lida.'),
34622
34711
  animationStyle: AnimationStyleEnum.optional().describe('Opcional. Estilo visual SOLO para presets animados ("pixar_problem" o "pixar_sassy") \u2014 la API lo rechaza con cualquier otro preset. Opciones: "pixar_3d" (default, look 3D de pel\xEDcula animada), "claymation" (plastilina stop-motion artesanal), "wes_anderson" (tableau sim\xE9trico pastel), "sci_fi" (futurista con HUDs y ne\xF3n), "realista" (fotorrealista, fiel al producto). Si el usuario pide un look animado sin nombrar el estilo, omite el campo.'),
34623
34712
  durationSeconds: z140.union([
34624
34713
  z140.literal(15),
@@ -34631,7 +34720,7 @@ var CreateProductTaskInput = z140.object({
34631
34720
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
34632
34721
  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).'),
34633
34722
  videoAngleCount: z140.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."),
34634
- 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.'),
34723
+ 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", "viral" (video sin voz: r\xF3tulos en pantalla y antes/despu\xE9s; solo Multiescena de 15 s y solo para cuentas habilitadas; la API rechaza otras combinaciones), "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.'),
34635
34724
  outputs: z140.array(z140.enum([
34636
34725
  "landing",
34637
34726
  "video",
@@ -34741,7 +34830,7 @@ var PendingActionPayload = z140.discriminatedUnion("kind", [
34741
34830
  // an opaque HTTP 400 after the model already believed it had been saved.
34742
34831
  features: z140.array(z140.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
34743
34832
  buyerPersona: z140.string().min(1).max(360).optional(),
34744
- narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora.'),
34833
+ narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora. "viral" (sin voz, r\xF3tulos y antes/despu\xE9s; solo Multiescena de 15 s) solo est\xE1 disponible para cuentas habilitadas y tareas del motor nuevo.'),
34745
34834
  actor: z140.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."),
34746
34835
  actorId: z140.string().max(120).nullable().optional(),
34747
34836
  creativeDirection: z140.string().max(2e3).optional(),
@@ -34751,10 +34840,10 @@ var PendingActionPayload = z140.discriminatedUnion("kind", [
34751
34840
  narrativeSelection: z140.enum([
34752
34841
  "market",
34753
34842
  "guided"
34754
- ]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo."),
34843
+ ]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo. Si la omites y la tarea tiene un estudio de mercado listo, el \xE1ngulo sale en market; repetir la narrativeStyle que propuso la IA no cuenta como elecci\xF3n."),
34755
34844
  angle: z140.string().min(1).max(480).optional(),
34756
34845
  buyerPersona: z140.string().min(1).max(360).optional(),
34757
- narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora.'),
34846
+ narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora. "viral" (sin voz, r\xF3tulos y antes/despu\xE9s; solo Multiescena de 15 s) solo est\xE1 disponible para cuentas habilitadas y tareas del motor nuevo.'),
34758
34847
  actor: z140.string().min(1).max(360).optional(),
34759
34848
  actorId: z140.string().max(120).nullable().optional(),
34760
34849
  creativeDirection: z140.string().max(2e3).optional()
@@ -36914,7 +37003,7 @@ var VideoCreativeSetup = z144.discriminatedUnion("mode", [
36914
37003
  }).strict(),
36915
37004
  z144.object({
36916
37005
  mode: z144.literal("guided"),
36917
- 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. Todas las narrativas funcionan en Un solo clip y Multiescena.'),
37006
+ 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. Todas las narrativas salvo "viral" funcionan en Un solo clip y Multiescena. "viral" es un video sin voz (r\xF3tulos en pantalla, antes/despu\xE9s), solo Multiescena de 15 s y solo para cuentas habilitadas; la API rechaza otras combinaciones.'),
36918
37007
  buyerPersona: z144.string().trim().min(1).max(360),
36919
37008
  animationStyle: AnimationStyleEnum.optional().describe('Opcional. Estilo visual SOLO para presets animados ("pixar_problem" o "pixar_sassy") \u2014 la API lo rechaza con cualquier otro preset. Opciones: "pixar_3d" (default, look 3D de pel\xEDcula animada), "claymation" (plastilina stop-motion artesanal), "wes_anderson" (tableau sim\xE9trico pastel), "sci_fi" (futurista con HUDs y ne\xF3n), "realista" (fotorrealista, fiel al producto). Si el usuario pide un look animado sin nombrar el estilo, omite el campo.'),
36920
37009
  presenter: VideoPresenter,
@@ -36936,7 +37025,7 @@ var VideoInput = z144.object({
36936
37025
  generateAiEdit: AiEditRequestSchema.optional().describe('Opcional. Pide la Edici\xF3n con IA: una pasada que a\xF1ade texto, efectos y m\xFAsica sobre cada video entregado (variante with_ai_edit; se cobra al entregar, por video). Nivel: "economy" (Econ\xF3mica, Sonnet 5.5, m\xE1s barata) o "pro" (Opus 5.5, mejor calidad, ~10 min). true equivale a "pro"; false/omitido = sin edici\xF3n.'),
36937
37026
  durationSeconds: z144.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."),
36938
37027
  videoAngleCount: z144.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."),
36939
- 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.'),
37028
+ 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", "viral" (video sin voz: r\xF3tulos en pantalla y antes/despu\xE9s; solo Multiescena de 15 s y solo para cuentas habilitadas; la API rechaza otras combinaciones), "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.'),
36940
37029
  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."),
36941
37030
  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.'),
36942
37031
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
@@ -37849,7 +37938,7 @@ var RegenerateVideoScriptInput = RegenerateVideoScriptCapabilityRequest.extend({
37849
37938
  });
37850
37939
  function registerScriptReviewTools(server, api) {
37851
37940
  server.registerTool("get_video_script", {
37852
- description: getVideoScriptGuidance.summary + ' S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId?, persuasiveFunction?, winnerVisual?, expectedVisual? }], error? }. En escenas copiadas de un winner, `persuasiveFunction` explica qu\xE9 logra ese beat, `winnerVisual` conserva la observaci\xF3n fuente y `expectedVisual` describe nuestra adaptaci\xF3n localizada; muestra los tres junto al di\xE1logo para que una edici\xF3n no desaline\xE9 guion y visual. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado; mu\xE9straselo al usuario.\n\nREGLA DURA: status="ready" es un estado terminal de interacci\xF3n. Presenta el guion una sola vez y NO vuelvas a llamar get_video_script ni get_task_status hasta que el usuario responda aprobar, editar o regenerar. La respuesta a\xF1ade interactionState="awaiting_user_review" y nextAction="present_script_and_wait".\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y llama este tool; su campo `status` es la se\xF1al real:\n- "ready" \u2192 presenta todas las escenas y DETENTE hasta la respuesta del usuario.\n- "not_ready" \u2192 espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle consulta otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar.\n- "error" \u2192 consulta `get_task_status` antes de ofrecer recuperaci\xF3n. No regeneres autom\xE1ticamente una tarea terminal.\n\nANGLE KEY: usa las claves expl\xEDcitas de reviewNextAction.calls, incluso si s\xF3lo seleccionaste un candidato. Cuando uno est\xE9 ready, detente en ese \xE1ngulo y espera la decisi\xF3n antes de consultar otro.',
37941
+ description: getVideoScriptGuidance.summary + ' S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId?, persuasiveFunction?, winnerVisual?, expectedVisual?, onScreenLabel? }], error? }. En videos \xABViral\xBB (sin voz) cada escena trae `onScreenLabel`: mu\xE9stralo como \xABTexto en pantalla\xBB; su `dialogue` viene vac\xEDo y no pidas di\xE1logo para esas escenas. En escenas copiadas de un winner, `persuasiveFunction` explica qu\xE9 logra ese beat, `winnerVisual` conserva la observaci\xF3n fuente y `expectedVisual` describe nuestra adaptaci\xF3n localizada; muestra los tres junto al di\xE1logo para que una edici\xF3n no desaline\xE9 guion y visual. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado; mu\xE9straselo al usuario.\n\nREGLA DURA: status="ready" es un estado terminal de interacci\xF3n. Presenta el guion una sola vez y NO vuelvas a llamar get_video_script ni get_task_status hasta que el usuario responda aprobar, editar o regenerar. La respuesta a\xF1ade interactionState="awaiting_user_review" y nextAction="present_script_and_wait".\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y llama este tool; su campo `status` es la se\xF1al real:\n- "ready" \u2192 presenta todas las escenas y DETENTE hasta la respuesta del usuario.\n- "not_ready" \u2192 espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle consulta otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar.\n- "error" \u2192 consulta `get_task_status` antes de ofrecer recuperaci\xF3n. No regeneres autom\xE1ticamente una tarea terminal.\n\nANGLE KEY: usa las claves expl\xEDcitas de reviewNextAction.calls, incluso si s\xF3lo seleccionaste un candidato. Cuando uno est\xE9 ready, detente en ese \xE1ngulo y espera la decisi\xF3n antes de consultar otro.',
37853
37942
  inputSchema: GetVideoScriptInput.shape,
37854
37943
  annotations: READS_INTERNAL
37855
37944
  }, async (input) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "12.11.0",
3
+ "version": "12.12.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "ProductMaker MCP server — cost products, generate landing pages, UGC videos, and ad creatives, and publish to Shopify, Meta Ads, and TikTok Ads.",