@productmaker/mcp 2.3.2 → 2.4.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.
package/dist/stdio.js CHANGED
@@ -150,10 +150,10 @@ var ApiClient = class {
150
150
  };
151
151
 
152
152
  // src/index.ts
153
- var MCP_VERSION = "2.3.2";
153
+ var MCP_VERSION = "2.4.0";
154
154
 
155
155
  // src/tools/tasks.ts
156
- import { z as z43 } from "zod";
156
+ import { z as z44 } from "zod";
157
157
 
158
158
  // src/resolve-image.ts
159
159
  import { lookup as dnsLookup } from "dns/promises";
@@ -1042,6 +1042,8 @@ var Angle = z6.object({
1042
1042
  buyerPersona: z6.string().min(1).max(360),
1043
1043
  /** Character description for video (role + appearance + attitude). Drives voice selection and Frame0Spec. */
1044
1044
  actor: z6.string().min(1).max(360).optional(),
1045
+ /** Saved custom actor (Actor.id) resolved for this angle. Its approved photo anchors the identity. */
1046
+ actorId: z6.string().optional(),
1045
1047
  // Binary only: voice catalog has no non-binary voices.
1046
1048
  // See packages/angles/src/index.ts + getAngles coercion in angle.repository.ts.
1047
1049
  gender: z6.enum([
@@ -1632,6 +1634,8 @@ var IngestOptions = z14.object({
1632
1634
  generateAdCopy: z14.boolean().default(true),
1633
1635
  generateAiMusic: z14.boolean().optional(),
1634
1636
  voiceId: VoiceId.optional(),
1637
+ /** Global saved custom actor (Actor.id). Default for all angles. */
1638
+ actorId: z14.string().optional(),
1635
1639
  isCombo: z14.boolean().default(false),
1636
1640
  productContext: z14.string().transform((value) => value.trim()).pipe(z14.string().max(PRODUCT_CONTEXT_MAX_CHARS)).optional(),
1637
1641
  creativeDirection: z14.string().transform((value) => value.trim()).pipe(z14.string().max(500)).optional(),
@@ -1646,7 +1650,7 @@ var IngestOptions = z14.object({
1646
1650
  "landing",
1647
1651
  "video",
1648
1652
  "image"
1649
- ])).min(2).max(3).optional(),
1653
+ ])).min(1).max(3).optional(),
1650
1654
  // Landing page output: 'classic' = image-based (default), 'code' = structured
1651
1655
  // code landing rendered by the theme app extension. Absent ⇒ classic.
1652
1656
  landingMode: LandingModeSchema.optional(),
@@ -1697,7 +1701,13 @@ var VideoOptionsOverride = z14.object({
1697
1701
  videoResolution: VideoResolution.optional(),
1698
1702
  voiceId: VoiceId.optional(),
1699
1703
  /** Actor/character description (role + appearance + attitude). Drives voice and video character. */
1700
- actor: z14.string().max(360).optional()
1704
+ actor: z14.string().max(360).optional(),
1705
+ /** Saved custom actor (Actor.id). Its approved photo anchors the identity.
1706
+ * `null` is an explicit REMOVAL (the user cleared the picker) and must be
1707
+ * sent as such: an omitted key means "not mentioned", which every writer
1708
+ * reads as "leave the stored selection alone". Same contract as
1709
+ * service-angles.controller.ts. */
1710
+ actorId: z14.string().nullable().optional()
1701
1711
  });
1702
1712
  var AngleVideoOptions = VideoOptionsOverride.extend({
1703
1713
  angleIndex: z14.number().int().nonnegative(),
@@ -1708,7 +1718,11 @@ var AngleVideoOptions = VideoOptionsOverride.extend({
1708
1718
  /** Per-angle buyer persona override (describes the target audience) */
1709
1719
  buyerPersona: z14.string().max(360).optional(),
1710
1720
  /** Per-angle actor/character description (role + appearance + attitude). Drives voice and video character. */
1711
- actor: z14.string().max(360).optional()
1721
+ actor: z14.string().max(360).optional(),
1722
+ /** Per-angle saved custom actor (Actor.id). Overrides the global actorId.
1723
+ * `null` = the user removed the personaje on this angle; it must NOT fall
1724
+ * back to the global default. Omitted = not mentioned. */
1725
+ actorId: z14.string().nullable().optional()
1712
1726
  });
1713
1727
  var RapidScriptBlueprintShotForApproval = z14.object({
1714
1728
  id: z14.string().min(1),
@@ -1771,6 +1785,8 @@ var IngestResumePayload = z14.object({
1771
1785
  voiceId: VoiceId.optional(),
1772
1786
  /** Global actor/character description (role + appearance + attitude). Default for all angles. */
1773
1787
  actor: z14.string().max(360).optional(),
1788
+ /** Global saved custom actor (Actor.id). Default for all angles. */
1789
+ actorId: z14.string().optional(),
1774
1790
  /** Custom angle override for landing/creative (uses this instead of first angle) */
1775
1791
  customAngleOverride: z14.string().min(1).max(480).optional(),
1776
1792
  /** Pain points to emphasize in landings, creatives, and videos */
@@ -2469,6 +2485,8 @@ var RefineAngleResponse = z20.object({
2469
2485
  painPoints: z20.array(z20.string()).optional(),
2470
2486
  benefits: z20.array(z20.string()).optional(),
2471
2487
  actor: z20.string().optional(),
2488
+ /** Saved custom actor (Actor.id) carried through the refine round-trip. */
2489
+ actorId: z20.string().optional(),
2472
2490
  buyerPersona: z20.string().optional()
2473
2491
  });
2474
2492
 
@@ -2848,7 +2866,9 @@ var PendingUserActionSchema = z23.discriminatedUnion("kind", [
2848
2866
  id: z23.string().uuid(),
2849
2867
  content: z23.string(),
2850
2868
  creativeDirection: z23.string(),
2851
- actor: z23.string()
2869
+ actor: z23.string(),
2870
+ /** Saved custom actor (Actor.id) backing this angle, when one was chosen. */
2871
+ actorId: z23.string().optional()
2852
2872
  })).min(1).max(3)
2853
2873
  }),
2854
2874
  z23.object({
@@ -3001,7 +3021,8 @@ var CostEstimationType = z24.enum([
3001
3021
  "landing-regenerate",
3002
3022
  "landing-creative",
3003
3023
  "campaign-copy",
3004
- "service-creative"
3024
+ "service-creative",
3025
+ "actor-grid"
3005
3026
  ]);
3006
3027
  var IngestEstimationOptions = z24.object({
3007
3028
  /** Number of product references that the extract stage will clean. */
@@ -3102,6 +3123,8 @@ var VideoCreativeEstimationOptions = z24.object({
3102
3123
  requiresAngles: z24.boolean().optional(),
3103
3124
  /** When false, omit the Meta/Whisper competitor-scan ceiling from the estimate. */
3104
3125
  competitorScanEnabled: z24.boolean().optional(),
3126
+ /** Quote the competitor scan even when product extraction is skipped (direct video auto path). */
3127
+ requiresCompetitorScan: z24.boolean().optional(),
3105
3128
  generateAiMusic: z24.boolean().optional(),
3106
3129
  /** Free mode burns one Whisper pass over the final clip when enabled. */
3107
3130
  addSubtitles: z24.boolean().optional()
@@ -3170,6 +3193,7 @@ var ServiceCreativeEstimationOptions = z24.object({
3170
3193
  mediaAssetCount: z24.number().int().min(0).max(8).optional(),
3171
3194
  uploadedVideoDurationSeconds: z24.number().min(0).max(240).optional()
3172
3195
  });
3196
+ var ActorGridEstimationOptions = z24.object({}).strict();
3173
3197
  var CostEstimationRequest = z24.discriminatedUnion("type", [
3174
3198
  z24.object({
3175
3199
  type: z24.literal("ingest"),
@@ -3206,6 +3230,10 @@ var CostEstimationRequest = z24.discriminatedUnion("type", [
3206
3230
  z24.object({
3207
3231
  type: z24.literal("service-creative"),
3208
3232
  options: ServiceCreativeEstimationOptions
3233
+ }),
3234
+ z24.object({
3235
+ type: z24.literal("actor-grid"),
3236
+ options: ActorGridEstimationOptions
3209
3237
  })
3210
3238
  ]);
3211
3239
  var CostEstimationResponse = z24.object({
@@ -3696,10 +3724,15 @@ var PresignUploadRequest = z31.object({
3696
3724
  fileName: z31.string().min(1).max(255),
3697
3725
  /** File size in bytes (validated against MAX_FILE_SIZE_BYTES) */
3698
3726
  fileSize: z31.number().int().positive().max(CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES),
3699
- /** Service uploads receive a user-bound asset ticket; ads keep the legacy path. */
3727
+ /**
3728
+ * Service uploads receive a user-bound asset ticket; ads keep the legacy path.
3729
+ * `actor` uploads the identity photo of a custom actor and is stored under
3730
+ * `actors/<userId>/…` so `AssetAccessGuard` can resolve its owner.
3731
+ */
3700
3732
  purpose: z31.enum([
3701
3733
  "ads",
3702
- "service"
3734
+ "service",
3735
+ "actor"
3703
3736
  ]).optional()
3704
3737
  });
3705
3738
  var PresignUploadResponse = z31.object({
@@ -4692,6 +4725,13 @@ var DEFAULT_PRICING = {
4692
4725
  ourCostCents: 3,
4693
4726
  userPriceCents: 6
4694
4727
  },
4728
+ // Custom actor identity photos (gpt-image-2 @ 1K, 4 candidates per attempt).
4729
+ // Recorded once per PRODUCED candidate. Without this row billingService.record
4730
+ // logs "No pricing rule found; skipping billing record" and the whole grid is free.
4731
+ "kie:image-generation:actor": {
4732
+ ourCostCents: 3,
4733
+ userPriceCents: 6
4734
+ },
4695
4735
  "kie:image-generation:seedream-5-lite-text-to-image": {
4696
4736
  ourCostCents: 3,
4697
4737
  userPriceCents: 6
@@ -5628,6 +5668,32 @@ var DEFAULT_PRICING = {
5628
5668
  ourCostCents: 1.5,
5629
5669
  userPriceCents: 3
5630
5670
  },
5671
+ // Frame0 per-person anatomy gate. Priced PER PANEL, like the inventory above and for the same
5672
+ // reason: it is one vision call per rendered panel, so quoting it per VIDEO would under-quote
5673
+ // by the panel count (~10x). Same single-image, short-answer shape as the inventory, so it
5674
+ // carries the same rate until measured otherwise.
5675
+ "openrouter:rapid-panel-anatomy-gate": {
5676
+ ourCostCents: 1.5,
5677
+ userPriceCents: 3
5678
+ },
5679
+ // Plan hand-budget audit: one TEXT call per shot (no image), so materially cheaper than the
5680
+ // vision passes above. Billed at the call site, therefore it MUST be quoted — an unquoted paid
5681
+ // call is the failure this repo already has on record.
5682
+ "openrouter:plan-hand-budget-audit": {
5683
+ ourCostCents: 0.3,
5684
+ userPriceCents: 0.6
5685
+ },
5686
+ // Hand-budget REPAIR: the rewrite that fires only on a shot whose brief demands more hand jobs
5687
+ // than its cast has hands. One TEXT call, same shape as the audit above but with the brief on
5688
+ // both sides (prompt in, prose out), so twice the rate rather than the audit's.
5689
+ //
5690
+ // Kept on its OWN key rather than folded into the audit for the reason the frame-inventory delta
5691
+ // is: the audit runs on every shot and the repair on a minority, so one blended rate would price
5692
+ // both wrong and neither line would be auditable against the ledger.
5693
+ "openrouter:rapid-hand-budget-repair": {
5694
+ ourCostCents: 0.6,
5695
+ userPriceCents: 1.2
5696
+ },
5631
5697
  // Unit = one shot-action flatten, once per video, quoted ONLY when frame inventories are on.
5632
5698
  // Carrying ~10 inventories as text raised that call from a 1.7078¢ production median to a
5633
5699
  // measured 3.1481¢ / 3.4551¢. This is the difference, kept on its own key so the base rate above
@@ -7058,6 +7124,7 @@ var MAX_SEGMENT_SECONDS = 3;
7058
7124
  var VideoEditStatusSchema = z38.enum([
7059
7125
  "pending",
7060
7126
  "processing",
7127
+ "assembling",
7061
7128
  "ready",
7062
7129
  "accepted",
7063
7130
  "discarded",
@@ -7085,6 +7152,13 @@ var VideoEditModeSchema = z38.enum([
7085
7152
  "wan_edit",
7086
7153
  "regenerate"
7087
7154
  ]);
7155
+ var VideoRegenerationEngineSchema = z38.enum([
7156
+ "free_text",
7157
+ "free_image",
7158
+ "single_call",
7159
+ "multi_shot"
7160
+ ]);
7161
+ var VideoRegenerationRouteFingerprintSchema = z38.string().trim().min(1).max(128);
7088
7162
  var RegenerateEditRequestSchema = z38.object({
7089
7163
  angleKey: z38.string().min(1),
7090
7164
  comment: z38.string().min(1).max(2e3)
@@ -7481,7 +7555,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
7481
7555
  {
7482
7556
  name: "create_product_task",
7483
7557
  confirmation: "credit_consuming",
7484
- schemaHash: "3f03c4886f65a030e03a531c02e0ef879aaf3f5820e90ab3ca04a02350e693e6"
7558
+ schemaHash: "50845af40b29f479592ca5e545d9c1af1fe631998802827affccb4641fc90c18"
7485
7559
  },
7486
7560
  {
7487
7561
  name: "create_service_video",
@@ -7496,7 +7570,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
7496
7570
  {
7497
7571
  name: "edit_task_draft",
7498
7572
  confirmation: "state_change",
7499
- schemaHash: "f1763564083dcaac2d1f4d5e6c4625a2d4ec0ea3eea273aadc0fb9d4ab6c7d47"
7573
+ schemaHash: "1c9343bdce51aac36ec6254557c8a904037746523168527f012b4c5b6361e98e"
7500
7574
  },
7501
7575
  {
7502
7576
  name: "estimate_creative_variant",
@@ -7558,6 +7632,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
7558
7632
  confirmation: "none",
7559
7633
  schemaHash: "0e7a1cb5ad5c6681473df5689336edcd7a63b8d68853359982e44bfd98a65db8"
7560
7634
  },
7635
+ {
7636
+ name: "list_actors",
7637
+ confirmation: "none",
7638
+ schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
7639
+ },
7561
7640
  {
7562
7641
  name: "list_meta_ad_accounts",
7563
7642
  confirmation: "none",
@@ -7626,6 +7705,68 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
7626
7705
  ];
7627
7706
  var PRODUCTMAKER_MCP_CAPABILITY_NAMES = PRODUCTMAKER_MCP_CAPABILITIES.map((capability) => capability.name);
7628
7707
 
7708
+ // ../../packages/types/src/actor.ts
7709
+ import { z as z42 } from "zod";
7710
+ var ActorStatusSchema = z42.enum([
7711
+ "draft",
7712
+ "generating",
7713
+ "ready",
7714
+ "failed"
7715
+ ]);
7716
+ var ACTOR_NAME_MAX_CHARS = 60;
7717
+ var ACTOR_DESCRIPTION_MAX_CHARS = 600;
7718
+ var ACTOR_CANDIDATE_COUNT = 4;
7719
+ var ActorGenderSchema = z42.enum([
7720
+ "female",
7721
+ "male",
7722
+ "non_binary"
7723
+ ]);
7724
+ var ActorSchema = z42.object({
7725
+ id: z42.string(),
7726
+ name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS),
7727
+ description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS),
7728
+ gender: ActorGenderSchema,
7729
+ voiceId: z42.string().nullable().optional(),
7730
+ brandId: z42.string().nullable().optional(),
7731
+ status: ActorStatusSchema,
7732
+ /**
7733
+ * A candidate-grid run is in flight RIGHT NOW.
7734
+ *
7735
+ * Separate from `status` because the two answer different questions:
7736
+ * `status` is the PUBLISHED state (a personaje being regenerated stays
7737
+ * `ready`, so it keeps serving the renders that already reference it), and
7738
+ * this flag is the live one the wizard polls. It is derived from a lock
7739
+ * timestamp with a TTL, so an orphaned run reads as `false` instead of
7740
+ * pinning the personaje forever.
7741
+ */
7742
+ generating: z42.boolean().optional(),
7743
+ photoUrl: z42.string().url().nullable().optional(),
7744
+ avatarUrl: z42.string().url().nullable().optional(),
7745
+ candidateUrls: z42.array(z42.string().url()).optional(),
7746
+ /** Indices of candidateUrls that failed the anatomy check (Task 6). */
7747
+ flaggedCandidateIndices: z42.array(z42.number().int()).optional()
7748
+ });
7749
+ var CreateActorRequest = z42.object({
7750
+ name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS),
7751
+ description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS).default(""),
7752
+ gender: ActorGenderSchema,
7753
+ voiceId: z42.string().optional(),
7754
+ brandId: z42.string().optional(),
7755
+ sourcePhotoKey: z42.string().optional()
7756
+ }).refine((v) => v.description.trim().length > 0 || !!v.sourcePhotoKey, {
7757
+ message: "Se necesita una descripci\xF3n o una foto"
7758
+ });
7759
+ var UpdateActorRequest = z42.object({
7760
+ name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS).optional(),
7761
+ description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS).optional(),
7762
+ gender: ActorGenderSchema.optional(),
7763
+ voiceId: z42.string().nullable().optional(),
7764
+ brandId: z42.string().nullable().optional()
7765
+ });
7766
+ var ApproveActorRequest = z42.object({
7767
+ candidateIndex: z42.number().int().min(0).max(ACTOR_CANDIDATE_COUNT - 1)
7768
+ });
7769
+
7629
7770
  // src/resolve-media.ts
7630
7771
  var MAX_MEDIA_BYTES = CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES;
7631
7772
  var MEDIA_FORMATS = [
@@ -7865,10 +8006,10 @@ async function resolveMediaRef(ref, opts) {
7865
8006
  __name(resolveMediaRef, "resolveMediaRef");
7866
8007
 
7867
8008
  // src/tools/shared.ts
7868
- import { z as z42 } from "zod";
7869
- var Lang = z42.string().min(2).max(10);
7870
- var Country = z42.string().min(2).max(3);
7871
- var LandingStrategyEnum = z42.enum([
8009
+ import { z as z43 } from "zod";
8010
+ var Lang = z43.string().min(2).max(10);
8011
+ var Country = z43.string().min(2).max(3);
8012
+ var LandingStrategyEnum = z43.enum([
7872
8013
  "auto",
7873
8014
  "classic",
7874
8015
  "pain_solution",
@@ -7877,12 +8018,12 @@ var LandingStrategyEnum = z42.enum([
7877
8018
  "offer_value",
7878
8019
  "lifestyle_identity"
7879
8020
  ]);
7880
- var LandingImageFormatEnum = z42.enum([
8021
+ var LandingImageFormatEnum = z43.enum([
7881
8022
  "9:16",
7882
8023
  "4:5",
7883
8024
  "1:1"
7884
8025
  ]);
7885
- var VideoPresetEnum = z42.enum([
8026
+ var VideoPresetEnum = z43.enum([
7886
8027
  "auto",
7887
8028
  "ugc_dynamic",
7888
8029
  "ugc_how_to",
@@ -7895,7 +8036,7 @@ var VideoPresetEnum = z42.enum([
7895
8036
  "pixar_problem",
7896
8037
  "pixar_sassy"
7897
8038
  ]);
7898
- var TaskCreativeVariantVideoPresetEnum = z42.enum([
8039
+ var TaskCreativeVariantVideoPresetEnum = z43.enum([
7899
8040
  "auto",
7900
8041
  "ugc_dynamic",
7901
8042
  "ugc_how_to",
@@ -7966,7 +8107,7 @@ function resolveVideoPreset(preset) {
7966
8107
  return VIDEO_PRESET_RESOLUTION[preset] ?? null;
7967
8108
  }
7968
8109
  __name(resolveVideoPreset, "resolveVideoPreset");
7969
- var VideoTierEnum = z42.enum([
8110
+ var VideoTierEnum = z43.enum([
7970
8111
  "basic"
7971
8112
  ]);
7972
8113
  function resolveVideoTier(_tier) {
@@ -7987,17 +8128,17 @@ function resolveVideoFormatForDuration(durationSeconds) {
7987
8128
  return durationSeconds === 30 ? "double_30s" : "single_15s";
7988
8129
  }
7989
8130
  __name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
7990
- var VideoResolutionEnum = z42.enum([
8131
+ var VideoResolutionEnum = z43.enum([
7991
8132
  "480p",
7992
8133
  "720p"
7993
8134
  ]);
7994
- var VideoStyleEnum = z42.enum([
8135
+ var VideoStyleEnum = z43.enum([
7995
8136
  "ugc",
7996
8137
  "cartoon_3d",
7997
8138
  "product_only",
7998
8139
  "ai_dynamic"
7999
8140
  ]);
8000
- var NarrativeStyleEnum = z42.enum([
8141
+ var NarrativeStyleEnum = z43.enum([
8001
8142
  "review",
8002
8143
  "ugc_how_to",
8003
8144
  "problem_product_cta",
@@ -8014,7 +8155,7 @@ var NarrativeStyleEnum = z42.enum([
8014
8155
  "ugc_skeptic_confession",
8015
8156
  "ugc_demo_review"
8016
8157
  ]);
8017
- var PublicNarrativeStyleEnum = z42.enum([
8158
+ var PublicNarrativeStyleEnum = z43.enum([
8018
8159
  "review",
8019
8160
  "ugc_how_to",
8020
8161
  "ugc_unboxing_chaos",
@@ -8026,7 +8167,7 @@ var PublicNarrativeStyleEnum = z42.enum([
8026
8167
  "animated_problem_pixar",
8027
8168
  "sassy_product_pixar"
8028
8169
  ]);
8029
- var VideoModelEnum = z42.enum([
8170
+ var VideoModelEnum = z43.enum([
8030
8171
  "veo",
8031
8172
  "wan",
8032
8173
  "bytedance",
@@ -8034,25 +8175,25 @@ var VideoModelEnum = z42.enum([
8034
8175
  "grok",
8035
8176
  "kling"
8036
8177
  ]);
8037
- var ProductReferenceUserChoiceEnum = z42.enum([
8178
+ var ProductReferenceUserChoiceEnum = z43.enum([
8038
8179
  "same_product",
8039
8180
  "variants",
8040
8181
  "different_products",
8041
8182
  "combo_distinct"
8042
8183
  ]);
8043
- var ProductReferenceCandidateGroupingEnum = z42.enum([
8184
+ var ProductReferenceCandidateGroupingEnum = z43.enum([
8044
8185
  "same_product_same_variant",
8045
8186
  "same_product_different_variants",
8046
8187
  "different_products",
8047
8188
  "unclear"
8048
8189
  ]);
8049
- var GapDurationChoiceMirror = z42.object({
8050
- angleIndex: z42.number().int().nonnegative(),
8051
- recommendedSec: z42.union([
8052
- z42.literal(15),
8053
- z42.literal(30)
8190
+ var GapDurationChoiceMirror = z43.object({
8191
+ angleIndex: z43.number().int().nonnegative(),
8192
+ recommendedSec: z43.union([
8193
+ z43.literal(15),
8194
+ z43.literal(30)
8054
8195
  ]),
8055
- estimatedDeltaCredits: z42.number().int().nonnegative().optional()
8196
+ estimatedDeltaCredits: z43.number().int().nonnegative().optional()
8056
8197
  });
8057
8198
  function buildSelectedImageUrlsHash(urls) {
8058
8199
  const input = [
@@ -8066,13 +8207,13 @@ function buildSelectedImageUrlsHash(urls) {
8066
8207
  return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
8067
8208
  }
8068
8209
  __name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
8069
- var ImageCreativeStyleEnum = z42.enum([
8210
+ var ImageCreativeStyleEnum = z43.enum([
8070
8211
  "studio",
8071
8212
  "floating",
8072
8213
  "ingredients",
8073
8214
  "in_use"
8074
8215
  ]);
8075
- var ImageCreativeArchetypeEnum = z42.enum([
8216
+ var ImageCreativeArchetypeEnum = z43.enum([
8076
8217
  "direct_response_product_ad",
8077
8218
  "hero_outcome",
8078
8219
  "offer_value_prop",
@@ -8081,23 +8222,23 @@ var ImageCreativeArchetypeEnum = z42.enum([
8081
8222
  "problem_solution",
8082
8223
  "lifestyle_identity"
8083
8224
  ]);
8084
- var ImageCreativeVisualTreatmentEnum = z42.enum([
8225
+ var ImageCreativeVisualTreatmentEnum = z43.enum([
8085
8226
  "performance_product_ad",
8086
8227
  "premium_dtc_carousel"
8087
8228
  ]);
8088
- var ImageCreativeAspectRatioEnum = z42.enum([
8229
+ var ImageCreativeAspectRatioEnum = z43.enum([
8089
8230
  "1:1",
8090
8231
  "4:5",
8091
8232
  "9:16",
8092
8233
  "16:9"
8093
8234
  ]);
8094
- var ImageRefSchema = z42.object({
8095
- url: z42.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.'),
8096
- path: z42.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`.')
8235
+ var ImageRefSchema = z43.object({
8236
+ url: z43.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.'),
8237
+ path: z43.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`.')
8097
8238
  }).refine((v) => Boolean(v.url) !== Boolean(v.path), {
8098
8239
  message: "each image entry must have exactly one of `url` or `path`"
8099
8240
  });
8100
- var ImagesInput = z42.array(ImageRefSchema).min(1).max(5).describe('1 a 5 fotos del MISMO producto. La PRIMERA es la principal (hero); las dem\xE1s son \xE1ngulos adicionales. NO mezcles productos distintos en una sola llamada.\n\nCada entrada tiene EXACTAMENTE uno de:\n\u2022 `url` \u2014 URL p\xFAblica HTTPS (Shopify CDN, Cloudinary). Prefi\xE9rela si la foto ya est\xE1 hospedada.\n\u2022 `path` \u2014 Ruta del archivo en el computador del usuario (ej. "/Users/maria/Desktop/foto.jpg", "~/Downloads/camiseta.png"). \xDAsala cuando el usuario te dijo d\xF3nde guard\xF3 la foto. Solo MCP local \u2014 el cliente web rechaza paths.\n\nSi el usuario te peg\xF3 la foto en el chat pero no te dio ni URL ni ruta: PREG\xDANTALE d\xF3nde la tiene guardada (Escritorio, Descargas, etc.). Si el `path` falla con IMAGE_PATH_NOT_FOUND, prueba 1-2 ubicaciones obvias alternativas (~/Desktop, ~/Downloads) antes de molestar al usuario. Tras 3 intentos fallidos, p\xEDdele la ruta completa.');
8241
+ var ImagesInput = z43.array(ImageRefSchema).min(1).max(5).describe('1 a 5 fotos del MISMO producto. La PRIMERA es la principal (hero); las dem\xE1s son \xE1ngulos adicionales. NO mezcles productos distintos en una sola llamada.\n\nCada entrada tiene EXACTAMENTE uno de:\n\u2022 `url` \u2014 URL p\xFAblica HTTPS (Shopify CDN, Cloudinary). Prefi\xE9rela si la foto ya est\xE1 hospedada.\n\u2022 `path` \u2014 Ruta del archivo en el computador del usuario (ej. "/Users/maria/Desktop/foto.jpg", "~/Downloads/camiseta.png"). \xDAsala cuando el usuario te dijo d\xF3nde guard\xF3 la foto. Solo MCP local \u2014 el cliente web rechaza paths.\n\nSi el usuario te peg\xF3 la foto en el chat pero no te dio ni URL ni ruta: PREG\xDANTALE d\xF3nde la tiene guardada (Escritorio, Descargas, etc.). Si el `path` falla con IMAGE_PATH_NOT_FOUND, prueba 1-2 ubicaciones obvias alternativas (~/Desktop, ~/Downloads) antes de molestar al usuario. Tras 3 intentos fallidos, p\xEDdele la ruta completa.');
8101
8242
  function asText(value) {
8102
8243
  return {
8103
8244
  content: [
@@ -8150,14 +8291,14 @@ async function submitCreative(api, opts) {
8150
8291
  });
8151
8292
  }
8152
8293
  __name(submitCreative, "submitCreative");
8153
- var ResearchTargetCountryEnum = z42.enum([
8294
+ var ResearchTargetCountryEnum = z43.enum([
8154
8295
  "CO",
8155
8296
  "MX",
8156
8297
  "PE",
8157
8298
  "EC",
8158
8299
  "CL"
8159
8300
  ]);
8160
- var ResearchCategoryEnum = z42.enum([
8301
+ var ResearchCategoryEnum = z43.enum([
8161
8302
  "auto",
8162
8303
  "automotive",
8163
8304
  "kitchen",
@@ -8769,52 +8910,53 @@ function renderOutputsMarkdown(outputs) {
8769
8910
  __name(renderOutputsMarkdown, "renderOutputsMarkdown");
8770
8911
 
8771
8912
  // src/tools/tasks.ts
8772
- var CreateProductTaskInput = z43.object({
8913
+ var CreateProductTaskInput = z44.object({
8773
8914
  images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
8774
- projectId: z43.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
8775
- productUrl: z43.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."),
8915
+ projectId: z44.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
8916
+ productUrl: z44.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."),
8776
8917
  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.'),
8777
8918
  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.'),
8778
- price: z43.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.'),
8779
- compareAtPrice: z43.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".'),
8780
- offer: z43.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.'),
8781
- paymentMethods: z43.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.'),
8782
- productContext: z43.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."),
8783
- productLabel: z43.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.'),
8919
+ price: z44.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.'),
8920
+ compareAtPrice: z44.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".'),
8921
+ offer: z44.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.'),
8922
+ paymentMethods: z44.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.'),
8923
+ productContext: z44.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."),
8924
+ productLabel: z44.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.'),
8784
8925
  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 y 30s, 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" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla). Si el usuario pide algo fuera de esta lista, p\xEDdele una opci\xF3n v\xE1lida.'),
8785
- durationSeconds: z43.union([
8786
- z43.literal(15),
8787
- z43.literal(30)
8926
+ durationSeconds: z44.union([
8927
+ z44.literal(15),
8928
+ z44.literal(30)
8788
8929
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
8789
- pauseForAngleReview: z43.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. \xDAsalo si quiere decidir 15/30s cuando aparezca un winner. Default false."),
8930
+ pauseForAngleReview: z44.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. \xDAsalo si quiere decidir 15/30s cuando aparezca un winner. Default false."),
8790
8931
  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.'),
8791
8932
  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).'),
8792
- multiAngleVideos: z43.boolean().optional().describe("Opcional. Si true, genera m\xFAltiples \xE1ngulos de venta en paralelo (3-5 videos). Si false/omitido, genera 1 video del \xE1ngulo principal. Default false."),
8933
+ multiAngleVideos: z44.boolean().optional().describe("Opcional. Si true, genera m\xFAltiples \xE1ngulos de venta en paralelo (3-5 videos). Si false/omitido, genera 1 video del \xE1ngulo principal. Default false."),
8793
8934
  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". angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. Requiere multiAngleVideos:true para varios \xE1ngulos.'),
8794
- outputs: z43.array(z43.enum([
8935
+ outputs: z44.array(z44.enum([
8795
8936
  "landing",
8796
8937
  "video",
8797
8938
  "image"
8798
8939
  ])).min(2).max(3).optional().describe('Opcional. Qu\xE9 outputs generar. Default: todos ["landing","video","image"]. Para 2 outputs: ["landing","image"] (sin video), ["video","image"] (sin landing), ["landing","video"] (sin imagen). Para 1 solo output, usa generate_landing / generate_video_creative / generate_image_creatives.'),
8799
- competitorScanEnabled: z43.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
8800
- brandId: z43.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."),
8801
- creativeDirection: z43.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.'),
8802
- headline: z43.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."),
8803
- subheadline: z43.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."),
8940
+ competitorScanEnabled: z44.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
8941
+ brandId: z44.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."),
8942
+ actorId: z44.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."),
8943
+ creativeDirection: z44.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.'),
8944
+ headline: z44.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."),
8945
+ subheadline: z44.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."),
8804
8946
  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".'),
8805
8947
  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".')
8806
8948
  }).strict();
8807
- var CreateServiceVideoInput = z43.object({
8808
- landingUrl: z43.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
8809
- additionalContext: z43.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
8949
+ var CreateServiceVideoInput = z44.object({
8950
+ landingUrl: z44.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
8951
+ additionalContext: z44.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
8810
8952
  language: Lang.describe("Idioma del video: es, en o pt."),
8811
- voiceId: z43.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.'),
8812
- brandId: z43.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
8813
- reviewMode: z43.enum([
8953
+ voiceId: z44.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.'),
8954
+ brandId: z44.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
8955
+ reviewMode: z44.enum([
8814
8956
  "automatic",
8815
8957
  "guided"
8816
8958
  ]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
8817
- videoPreset: z43.enum([
8959
+ videoPreset: z44.enum([
8818
8960
  "auto",
8819
8961
  "ugc_dynamic",
8820
8962
  "ugc_how_to",
@@ -8822,34 +8964,35 @@ var CreateServiceVideoInput = z43.object({
8822
8964
  "wild_card"
8823
8965
  ]).default("auto")
8824
8966
  }).strict();
8825
- var GetTaskStatusInput = z43.object({
8826
- taskId: z43.string().min(1),
8827
- waitSeconds: z43.number().int().min(0).max(120).optional()
8828
- });
8829
- var ListTasksInput = z43.object({
8830
- limit: z43.number().int().min(1).max(50).optional(),
8831
- offset: z43.number().int().min(0).optional(),
8832
- search: z43.string().optional()
8833
- });
8834
- var EditTaskDraftInput = z43.object({
8835
- taskId: z43.string().min(1),
8836
- angleIndex: z43.number().int().min(0),
8837
- angle: z43.object({
8838
- angle: z43.string().min(1).max(480),
8839
- buyerPersona: z43.string().min(1).max(360),
8840
- actor: z43.string().min(1).max(360).optional(),
8841
- gender: z43.enum([
8967
+ var GetTaskStatusInput = z44.object({
8968
+ taskId: z44.string().min(1),
8969
+ waitSeconds: z44.number().int().min(0).max(120).optional()
8970
+ });
8971
+ var ListTasksInput = z44.object({
8972
+ limit: z44.number().int().min(1).max(50).optional(),
8973
+ offset: z44.number().int().min(0).optional(),
8974
+ search: z44.string().optional()
8975
+ });
8976
+ var EditTaskDraftInput = z44.object({
8977
+ taskId: z44.string().min(1),
8978
+ angleIndex: z44.number().int().min(0),
8979
+ angle: z44.object({
8980
+ angle: z44.string().min(1).max(480),
8981
+ buyerPersona: z44.string().min(1).max(360),
8982
+ actor: z44.string().min(1).max(360).optional(),
8983
+ actorId: z44.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."),
8984
+ gender: z44.enum([
8842
8985
  "female",
8843
8986
  "male"
8844
8987
  ]).optional(),
8845
- scriptNotes: z43.string().max(2e3).optional()
8988
+ scriptNotes: z44.string().max(2e3).optional()
8846
8989
  })
8847
8990
  });
8848
- var PendingActionPayload = z43.discriminatedUnion("kind", [
8849
- z43.object({
8850
- kind: z43.literal("product_reference_confirmation"),
8991
+ var PendingActionPayload = z44.discriminatedUnion("kind", [
8992
+ z44.object({
8993
+ kind: z44.literal("product_reference_confirmation"),
8851
8994
  userChoice: ProductReferenceUserChoiceEnum,
8852
- confirmedProductNames: z43.array(z43.string().min(1).max(120)).min(2).max(8).optional(),
8995
+ confirmedProductNames: z44.array(z44.string().min(1).max(120)).min(2).max(8).optional(),
8853
8996
  // SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
8854
8997
  // reachable via MCP. Valid ONLY together with userChoice='variants' (same
8855
8998
  // product in several colors): renders one shot per color and stitches them,
@@ -8860,50 +9003,50 @@ var PendingActionPayload = z43.discriminatedUnion("kind", [
8860
9003
  // safe because multi-scene IS the intended render for a color variant set,
8861
9004
  // not a blind quality upsell. Maps to `videoTier: 'standard'` on the
8862
9005
  // confirm call; the backend re-gates it (variants + variant_set only).
8863
- useMultiSceneShowcase: z43.boolean().optional().describe('Opcional (SOLO con userChoice="variants"). true = render Multiescena: una toma por color, mostrando todas las variantes, hasta 30s. Se factura POR ESCENA (m\xE1s caro que el render normal de una sola toma) \u2014 AV\xCDSALE del mayor costo al usuario antes de activarlo. Omitir = render est\xE1ndar de una sola toma. Se ignora si userChoice no es "variants".')
9006
+ useMultiSceneShowcase: z44.boolean().optional().describe('Opcional (SOLO con userChoice="variants"). true = render Multiescena: una toma por color, mostrando todas las variantes, hasta 30s. Se factura POR ESCENA (m\xE1s caro que el render normal de una sola toma) \u2014 AV\xCDSALE del mayor costo al usuario antes de activarlo. Omitir = render est\xE1ndar de una sola toma. Se ignora si userChoice no es "variants".')
8864
9007
  }),
8865
- z43.object({
8866
- kind: z43.literal("angle_review_approval"),
9008
+ z44.object({
9009
+ kind: z44.literal("angle_review_approval"),
8867
9010
  // All edits are optional — empty action body just approves the
8868
9011
  // backend-generated draft as-is. Field shapes mirror the angle-review
8869
9012
  // request contracts in @packages/types/wizard.ts; the handler routes to
8870
9013
  // the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
8871
- customAngle: z43.string().min(1).max(480).optional(),
9014
+ customAngle: z44.string().min(1).max(480).optional(),
8872
9015
  // Keep the shared MCP action within the strictest downstream contract:
8873
9016
  // autopilot/image resume endpoints cap these fields at 1500 characters.
8874
- painPoints: z43.string().max(1500).optional(),
8875
- benefits: z43.string().max(1500).optional(),
8876
- features: z43.array(z43.string().min(1).max(300)).max(10).optional(),
8877
- gapDurationSeconds: z43.union([
8878
- z43.literal(15),
8879
- z43.literal(30)
9017
+ painPoints: z44.string().max(1500).optional(),
9018
+ benefits: z44.string().max(1500).optional(),
9019
+ features: z44.array(z44.string().min(1).max(300)).max(10).optional(),
9020
+ gapDurationSeconds: z44.union([
9021
+ z44.literal(15),
9022
+ z44.literal(30)
8880
9023
  ]).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.")
8881
9024
  }),
8882
- z43.object({
8883
- kind: z43.literal("service_angle_approval"),
8884
- angleId: z43.string().uuid(),
8885
- content: z43.string().min(1).max(480).optional(),
8886
- creativeDirection: z43.string().min(1).max(1500).optional(),
8887
- actor: z43.string().min(1).max(360).optional()
9025
+ z44.object({
9026
+ kind: z44.literal("service_angle_approval"),
9027
+ angleId: z44.string().uuid(),
9028
+ content: z44.string().min(1).max(480).optional(),
9029
+ creativeDirection: z44.string().min(1).max(1500).optional(),
9030
+ actor: z44.string().min(1).max(360).optional()
8888
9031
  }),
8889
- z43.object({
8890
- kind: z43.literal("service_script_approval"),
8891
- voiceovers: z43.record(z43.string(), z43.string().min(1).max(600)).optional(),
8892
- visualDirections: z43.record(z43.string(), z43.string().min(1).max(1200)).optional()
9032
+ z44.object({
9033
+ kind: z44.literal("service_script_approval"),
9034
+ voiceovers: z44.record(z44.string(), z44.string().min(1).max(600)).optional(),
9035
+ visualDirections: z44.record(z44.string(), z44.string().min(1).max(1200)).optional()
8893
9036
  })
8894
9037
  ]);
8895
- var ResolvePendingActionInput = z43.object({
8896
- taskId: z43.string().min(1),
9038
+ var ResolvePendingActionInput = z44.object({
9039
+ taskId: z44.string().min(1),
8897
9040
  action: PendingActionPayload
8898
9041
  }).strict();
8899
9042
  function resolveVariantConfirmVideoTier(action) {
8900
9043
  return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
8901
9044
  }
8902
9045
  __name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
8903
- var StatusForResolveSchema = z43.object({
9046
+ var StatusForResolveSchema = z44.object({
8904
9047
  // Metadata used to select the backend resume contract. Older API responses
8905
9048
  // may omit these fields, so the resolver keeps the generic fallback.
8906
- kind: z43.enum([
9049
+ kind: z44.enum([
8907
9050
  "autopilot",
8908
9051
  "video",
8909
9052
  "image",
@@ -8911,24 +9054,24 @@ var StatusForResolveSchema = z43.object({
8911
9054
  "mix",
8912
9055
  "service"
8913
9056
  ]).optional(),
8914
- selectedOutputs: z43.array(z43.enum([
9057
+ selectedOutputs: z44.array(z44.enum([
8915
9058
  "landing",
8916
9059
  "video",
8917
9060
  "image"
8918
9061
  ])).optional(),
8919
- videoAngleCount: z43.number().int().min(1).max(3).optional(),
8920
- ugcFormat: z43.enum([
9062
+ videoAngleCount: z44.number().int().min(1).max(3).optional(),
9063
+ ugcFormat: z44.enum([
8921
9064
  "single_8s",
8922
9065
  "single_15s",
8923
9066
  "double_30s"
8924
9067
  ]).optional(),
8925
- videoStyle: z43.enum([
9068
+ videoStyle: z44.enum([
8926
9069
  "ugc",
8927
9070
  "cartoon_3d",
8928
9071
  "product_only",
8929
9072
  "ai_dynamic"
8930
9073
  ]).optional(),
8931
- videoModel: z43.enum([
9074
+ videoModel: z44.enum([
8932
9075
  "veo",
8933
9076
  "wan",
8934
9077
  "bytedance",
@@ -8936,39 +9079,45 @@ var StatusForResolveSchema = z43.object({
8936
9079
  "grok",
8937
9080
  "kling"
8938
9081
  ]).optional(),
8939
- videoTier: z43.enum([
9082
+ videoTier: z44.enum([
8940
9083
  "legacy",
8941
9084
  "standard",
8942
9085
  "pro",
8943
9086
  "single_call"
8944
9087
  ]).optional(),
8945
- videoResolution: z43.enum([
9088
+ videoResolution: z44.enum([
8946
9089
  "480p",
8947
9090
  "720p"
8948
9091
  ]).optional(),
8949
9092
  narrativeStyle: NarrativeStyleEnum.optional(),
8950
- pendingUserAction: z43.object({
8951
- kind: z43.string(),
8952
- candidateGrouping: z43.string().optional(),
8953
- selectedImageUrls: z43.array(z43.string()).optional(),
8954
- proposedProductNames: z43.array(z43.string()).optional(),
8955
- customAngleDraft: z43.string().optional(),
8956
- painPointsDraft: z43.string().optional(),
8957
- benefitsDraft: z43.string().optional(),
8958
- featuresDraft: z43.array(z43.string()).optional(),
9093
+ pendingUserAction: z44.object({
9094
+ kind: z44.string(),
9095
+ candidateGrouping: z44.string().optional(),
9096
+ selectedImageUrls: z44.array(z44.string()).optional(),
9097
+ proposedProductNames: z44.array(z44.string()).optional(),
9098
+ customAngleDraft: z44.string().optional(),
9099
+ painPointsDraft: z44.string().optional(),
9100
+ benefitsDraft: z44.string().optional(),
9101
+ featuresDraft: z44.array(z44.string()).optional(),
8959
9102
  // Recommendation consumed only when the caller explicitly sends
8960
9103
  // gapDurationSeconds: 30 in the angle approval action.
8961
9104
  gapDurationChoice: GapDurationChoiceMirror.optional(),
8962
- angles: z43.array(z43.object({
8963
- id: z43.string().uuid(),
8964
- content: z43.string(),
8965
- creativeDirection: z43.string(),
8966
- actor: z43.string()
8967
- })).optional(),
8968
- panels: z43.array(z43.object({
8969
- beatId: z43.string(),
8970
- voiceover: z43.string(),
8971
- visualDirection: z43.string()
9105
+ // `.passthrough()` + an explicit `actorId`: this object is a MIRROR of the
9106
+ // backend's pending service angle, and a bare z.object() STRIPS unknown
9107
+ // keys. `actorId` (the saved custom actor the picker stored) was silently
9108
+ // dropped here, so the approval handler below could not echo it back on the
9109
+ // PATCH — the selection looked accepted and reached no render.
9110
+ angles: z44.array(z44.object({
9111
+ id: z44.string().uuid(),
9112
+ content: z44.string(),
9113
+ creativeDirection: z44.string(),
9114
+ actor: z44.string(),
9115
+ actorId: z44.string().nullable().optional()
9116
+ }).passthrough()).optional(),
9117
+ panels: z44.array(z44.object({
9118
+ beatId: z44.string(),
9119
+ voiceover: z44.string(),
9120
+ visualDirection: z44.string()
8972
9121
  }).passthrough()).optional()
8973
9122
  }).optional()
8974
9123
  }).passthrough();
@@ -9044,6 +9193,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
9044
9193
  "options.selectedOutputs": input.outputs,
9045
9194
  "options.competitorScanEnabled": input.competitorScanEnabled,
9046
9195
  "options.brandId": input.brandId,
9196
+ "options.actorId": input.actorId,
9047
9197
  "options.creativeDirection": input.creativeDirection,
9048
9198
  "options.headline": input.headline,
9049
9199
  "options.subheadline": input.subheadline,
@@ -9185,7 +9335,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
9185
9335
  throw translateError(e);
9186
9336
  }
9187
9337
  });
9188
- server.tool("edit_task_draft", "Edita el draft de un angle de marketing antes de la generaci\xF3n. Campos editables: angle (texto del angle), buyerPersona, actor (descripci\xF3n del personaje), gender (female|male), scriptNotes. Use this only before generation completes. Editing already-generated images or videos is not supported.", EditTaskDraftInput.shape, async (input) => {
9338
+ server.tool("edit_task_draft", "Edita el draft de un angle de marketing antes de la generaci\xF3n. Campos editables: angle (texto del angle), buyerPersona, actor (descripci\xF3n del personaje), actorId (ID de un personaje guardado; usa list_actors), gender (female|male), scriptNotes. Use this only before generation completes. Editing already-generated images or videos is not supported.", EditTaskDraftInput.shape, async (input) => {
9189
9339
  try {
9190
9340
  const angle = await api.patchJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/angles/${input.angleIndex}`, input.angle ?? {});
9191
9341
  return asText({
@@ -9361,7 +9511,13 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
9361
9511
  const edits = {
9362
9512
  content: action.content ?? candidate.content,
9363
9513
  creativeDirection: action.creativeDirection ?? candidate.creativeDirection,
9364
- actor: action.actor ?? candidate.actor
9514
+ actor: action.actor ?? candidate.actor,
9515
+ // Echo the persisted saved actor. Omitted when absent so the PATCH
9516
+ // never writes a null over a selection made elsewhere — the
9517
+ // backend only updates the keys it receives.
9518
+ ...candidate.actorId ? {
9519
+ actorId: candidate.actorId
9520
+ } : {}
9365
9521
  };
9366
9522
  await api.patchJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-angles/${encodeURIComponent(candidate.id)}`, edits);
9367
9523
  const result = await api.postJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-angles/select`, {
@@ -9402,58 +9558,58 @@ __name(registerTaskTools, "registerTaskTools");
9402
9558
 
9403
9559
  // src/tools/creatives.ts
9404
9560
  import { randomUUID } from "crypto";
9405
- import { z as z44 } from "zod";
9561
+ import { z as z45 } from "zod";
9406
9562
  var CreativeRefs = {
9407
9563
  images: ImagesInput,
9408
9564
  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.'),
9409
9565
  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.'),
9410
- price: z44.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.'),
9411
- offer: z44.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.'),
9412
- compareAtPrice: z44.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
9413
- paymentMethods: z44.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago a mostrar. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
9414
- productContext: z44.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."),
9415
- productLabel: z44.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.'),
9416
- buyerPersona: z44.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.'),
9417
- isCombo: z44.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
9418
- competitorScanEnabled: z44.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
9419
- brandId: z44.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."),
9420
- projectId: z44.string().uuid().optional().describe("Opcional. ID de un proyecto guiado de dropshipping activo del usuario. Enlaza esta generaci\xF3n con su progreso y recompensas."),
9421
- creativeDirection: z44.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Aplica SOLO a im\xE1genes y landing. Ej. im\xE1genes: "amarillo, azul y rojo, minimalista"; landing: "elegante, paleta fr\xEDa". Para video usa scriptNotes (ambiente/escenario). M\xE1x 500 caracteres.'),
9422
- headline: z44.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar en la imagen (o en la portada de la landing). Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
9423
- subheadline: z44.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.")
9566
+ price: z45.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.'),
9567
+ offer: z45.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.'),
9568
+ compareAtPrice: z45.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
9569
+ paymentMethods: z45.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago a mostrar. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
9570
+ productContext: z45.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."),
9571
+ productLabel: z45.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.'),
9572
+ buyerPersona: z45.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.'),
9573
+ isCombo: z45.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
9574
+ competitorScanEnabled: z45.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
9575
+ brandId: z45.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."),
9576
+ projectId: z45.string().uuid().optional().describe("Opcional. ID de un proyecto guiado de dropshipping activo del usuario. Enlaza esta generaci\xF3n con su progreso y recompensas."),
9577
+ creativeDirection: z45.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Aplica SOLO a im\xE1genes y landing. Ej. im\xE1genes: "amarillo, azul y rojo, minimalista"; landing: "elegante, paleta fr\xEDa". Para video usa scriptNotes (ambiente/escenario). M\xE1x 500 caracteres.'),
9578
+ headline: z45.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar en la imagen (o en la portada de la landing). Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
9579
+ subheadline: z45.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.")
9424
9580
  };
9425
- var VideoInput = z44.object({
9581
+ var VideoInput = z45.object({
9426
9582
  images: CreativeRefs.images,
9427
- title: z44.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
9428
- primaryAngle: z44.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".'),
9583
+ title: z45.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
9584
+ primaryAngle: z45.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".'),
9429
9585
  language: CreativeRefs.language,
9430
9586
  country: CreativeRefs.country,
9431
- aspectRatio: z44.enum([
9587
+ aspectRatio: z45.enum([
9432
9588
  "9:16",
9433
9589
  "16:9",
9434
9590
  "Auto"
9435
9591
  ]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
9436
- durationSeconds: z44.union([
9437
- z44.literal(15),
9438
- z44.literal(30)
9592
+ durationSeconds: z45.union([
9593
+ z45.literal(15),
9594
+ z45.literal(30)
9439
9595
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
9440
9596
  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 y 30s, 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" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla).'),
9441
9597
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista"; para durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
9442
9598
  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).'),
9443
- features: z44.array(z44.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
9599
+ features: z45.array(z45.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
9444
9600
  price: CreativeRefs.price,
9445
9601
  offer: CreativeRefs.offer,
9446
9602
  productContext: CreativeRefs.productContext,
9447
9603
  productLabel: CreativeRefs.productLabel,
9448
- cta: z44.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.'),
9449
- scriptNotes: z44.string().optional().describe('Opcional. Notas/restricciones para el guion (la IA las usa al redactar). Ej. "No mencionar la marca X", "Tono divertido, no cl\xEDnico", "Mencionar 2x1". NO es el guion literal \u2014 para editar el guion palabra-por-palabra, pasa `pauseForVideoScriptReview: true` y usa get_video_script / approve_video_script tras la pausa.'),
9450
- pauseForVideoScriptReview: z44.boolean().optional().describe("Opcional. Si true, la tarea pausa despu\xE9s de generar el guion y antes de renderizar. \xDAsalo cuando el usuario quiera editar el guion antes de gastar cr\xE9ditos en el video. Tras la pausa: get_video_script para leerlo, approve_video_script para aprobar (con o sin ediciones). Default false."),
9451
- pauseForAngleReview: z44.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. Default false."),
9604
+ cta: z45.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.'),
9605
+ scriptNotes: z45.string().optional().describe('Opcional. Notas/restricciones para el guion (la IA las usa al redactar). Ej. "No mencionar la marca X", "Tono divertido, no cl\xEDnico", "Mencionar 2x1". NO es el guion literal \u2014 para editar el guion palabra-por-palabra, pasa `pauseForVideoScriptReview: true` y usa get_video_script / approve_video_script tras la pausa.'),
9606
+ pauseForVideoScriptReview: z45.boolean().optional().describe("Opcional. Si true, la tarea pausa despu\xE9s de generar el guion y antes de renderizar. \xDAsalo cuando el usuario quiera editar el guion antes de gastar cr\xE9ditos en el video. Tras la pausa: get_video_script para leerlo, approve_video_script para aprobar (con o sin ediciones). Default false."),
9607
+ pauseForAngleReview: z45.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. Default false."),
9452
9608
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
9453
9609
  brandId: CreativeRefs.brandId,
9454
9610
  projectId: CreativeRefs.projectId
9455
9611
  }).strict();
9456
- var FreeVideoQualityEnum = z44.enum([
9612
+ var FreeVideoQualityEnum = z45.enum([
9457
9613
  "economical",
9458
9614
  "high_detail"
9459
9615
  ]);
@@ -9475,38 +9631,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
9475
9631
  15
9476
9632
  ]
9477
9633
  };
9478
- var FreeVideoInput = z44.object({
9634
+ var FreeVideoInput = z45.object({
9479
9635
  images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
9480
- rawPrompt: z44.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."),
9636
+ rawPrompt: z45.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."),
9481
9637
  quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
9482
- durationSeconds: z44.number().int().min(1).max(15).describe("Duraci\xF3n exacta. Econ\xF3mico permite 6, 8, 10 o 15s; Alto detalle permite 4, 8, 12 o 15s."),
9638
+ durationSeconds: z45.number().int().min(1).max(15).describe("Duraci\xF3n exacta. Econ\xF3mico permite 6, 8, 10 o 15s; Alto detalle permite 4, 8, 12 o 15s."),
9483
9639
  videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
9484
- generateAiMusic: z44.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
9485
- addSubtitles: z44.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
9486
- idempotencyKey: z44.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.")
9640
+ generateAiMusic: z45.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
9641
+ addSubtitles: z45.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
9642
+ idempotencyKey: z45.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.")
9487
9643
  }).strict();
9488
9644
  var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
9489
9645
  var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
9490
9646
  var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
9491
9647
  var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
9492
- var ImageInput = z44.object({
9648
+ var ImageInput = z45.object({
9493
9649
  images: CreativeRefs.images,
9494
- productTitle: z44.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
9495
- primaryAngle: z44.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
9650
+ productTitle: z45.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
9651
+ primaryAngle: z45.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
9496
9652
  buyerPersona: CreativeRefs.buyerPersona,
9497
9653
  language: CreativeRefs.language,
9498
9654
  country: CreativeRefs.country,
9499
- nVariants: z44.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
9500
- imageModel: z44.string().optional().describe('Opcional. Modelo de imagen. Ej. "gpt-image-2", "nano-banana-2". Om\xEDtelo para usar el default optimizado por costo/calidad.'),
9655
+ nVariants: z45.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
9656
+ imageModel: z45.string().optional().describe('Opcional. Modelo de imagen. Ej. "gpt-image-2", "nano-banana-2". Om\xEDtelo para usar el default optimizado por costo/calidad.'),
9501
9657
  imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
9502
9658
  creativeArchetype: ImageCreativeArchetypeEnum.optional().describe(`Opcional. Arquetipo CRO de la composici\xF3n. Valores: ${archetypeList}. Si se omite, la IA escoge seg\xFAn \xE1ngulo+oferta (ej. "offer_value_prop" cuando hay descuento, "social_proof_trust" cuando el \xE1ngulo es testimonial).`),
9503
9659
  visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe(`Opcional. Tratamiento gr\xE1fico. Valores: ${visualTreatmentList} ("performance_product_ad" = dealer-style alto CTR para Meta/TikTok ads; "premium_dtc_carousel" = fotograf\xEDa limpia estilo Allbirds/Glossier). Si se omite, la IA escoge.`),
9504
- styleMix: z44.boolean().optional().describe("Opcional. Si true, las N variantes mezclan distintos imageStyle para A/B test. Si false/omitido, todas comparten estilo. Default false."),
9660
+ styleMix: z45.boolean().optional().describe("Opcional. Si true, las N variantes mezclan distintos imageStyle para A/B test. Si false/omitido, todas comparten estilo. Default false."),
9505
9661
  aspectRatio: ImageCreativeAspectRatioEnum.optional().describe(`Opcional. Relaci\xF3n de aspecto. Valores: ${aspectRatioList}. Default "4:5" (vertical Instagram feed).`),
9506
- generateAdCopy: z44.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
9507
- pauseForAngleReview: z44.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo y antes de las im\xE1genes para que el usuario edite v\xEDa edit_task_draft. Default false."),
9508
- features: z44.array(z44.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
9509
- brandColors: z44.array(z44.string()).optional().describe('Opcional. Paleta de marca en hex. Ej. ["#FF6B35", "#004E89"]. Si se omite, la IA elige seg\xFAn el producto.'),
9662
+ generateAdCopy: z45.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
9663
+ pauseForAngleReview: z45.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo y antes de las im\xE1genes para que el usuario edite v\xEDa edit_task_draft. Default false."),
9664
+ features: z45.array(z45.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
9665
+ brandColors: z45.array(z45.string()).optional().describe('Opcional. Paleta de marca en hex. Ej. ["#FF6B35", "#004E89"]. Si se omite, la IA elige seg\xFAn el producto.'),
9510
9666
  price: CreativeRefs.price,
9511
9667
  compareAtPrice: CreativeRefs.compareAtPrice,
9512
9668
  offer: CreativeRefs.offer,
@@ -9520,16 +9676,16 @@ var ImageInput = z44.object({
9520
9676
  headline: CreativeRefs.headline,
9521
9677
  subheadline: CreativeRefs.subheadline
9522
9678
  });
9523
- var EditImageCreativeInput = z44.object({
9524
- taskId: z44.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
9525
- variantIndex: z44.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
9526
- instruction: z44.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".')
9679
+ var EditImageCreativeInput = z45.object({
9680
+ taskId: z45.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
9681
+ variantIndex: z45.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
9682
+ instruction: z45.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".')
9527
9683
  });
9528
- var LandingInput = z44.object({
9684
+ var LandingInput = z45.object({
9529
9685
  images: CreativeRefs.images,
9530
9686
  language: CreativeRefs.language,
9531
9687
  country: CreativeRefs.country,
9532
- salesAngle: z44.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.'),
9688
+ salesAngle: z45.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.'),
9533
9689
  buyerPersona: CreativeRefs.buyerPersona,
9534
9690
  price: CreativeRefs.price,
9535
9691
  compareAtPrice: CreativeRefs.compareAtPrice,
@@ -9578,7 +9734,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
9578
9734
  throw translateError(e);
9579
9735
  }
9580
9736
  });
9581
- server.tool("generate_video_creative", 'Genera UN video creativo independiente. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), title, primaryAngle, language, country, aspectRatio.\n\nANTES de invocar, PREG\xDANTALE al usuario por price y offer si no los mencion\xF3 \u2014 son los inputs de mayor impacto en conversi\xF3n y nunca debes asumirlos. NO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente. Si el usuario te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos): durationSeconds (15 o 30), videoPreset (tipo/narrativa del video), features, cta, scriptNotes (dirige el guion Y el ambiente/escenario/est\xE9tica del video, ej. "en una playa, opulento").\n\nESTILO: 15s usa "Realista" (single_call); 30s usa "Multiescena" (multi-toma, aproximadamente 30s). Multiescena se factura por escena, as\xED que avisa el costo antes de activarlo.\n\nGUION: el usuario NO escribe el guion desde cero \u2014 la IA lo redacta. Si quiere REVISARLO/EDITARLO antes de renderizar (igual que en la web), pasa `pauseForVideoScriptReview: true`; cuando la tarea quede pausada, usa get_video_script para leer las escenas, ofr\xE9celas al usuario, y llama approve_video_script con las ediciones. Para dirigir el guion ANTES de generar (sin pausar), usa scriptNotes. Para revisar el \xE1ngulo y decidir duraci\xF3n tras el an\xE1lisis de competencia, pasa `pauseForAngleReview: true` y luego resuelve `angle_review_approval` con `gapDurationSeconds`.', VideoInput.shape, async (input) => {
9737
+ server.tool("generate_video_creative", 'Genera UN video creativo independiente. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), title, primaryAngle, language, country, aspectRatio.\n\nANTES de invocar, PREG\xDANTALE al usuario por price y offer si no los mencion\xF3 \u2014 son los inputs de mayor impacto en conversi\xF3n y nunca debes asumirlos. NO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente. Si el usuario te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos): durationSeconds (15 o 30), videoPreset (tipo/narrativa del video), features, cta, scriptNotes (dirige el guion Y el ambiente/escenario/est\xE9tica del video, ej. "en una playa, opulento").\n\nCOMPETENCIA Y GAP REPLICATION: con videoPreset="auto" (default) y competitorScanEnabled=true (default), el sistema analiza anuncios de competencia. Si encuentra un winner de video coherente, ancla el primaryAngle y genera autom\xE1ticamente la narrativa `gap_replication` para copiar su estructura audiovisual. No la selecciones manualmente: es un resultado del an\xE1lisis. Si competitorScanEnabled=false, esta b\xFAsqueda se omite.\n\nESTILO: 15s usa "Realista" (single_call); 30s usa "Multiescena" (multi-toma, aproximadamente 30s). Multiescena se factura por escena, as\xED que avisa el costo antes de activarlo.\n\nGUION: el usuario NO escribe el guion desde cero \u2014 la IA lo redacta. Si quiere REVISARLO/EDITARLO antes de renderizar (igual que en la web), pasa `pauseForVideoScriptReview: true`; cuando la tarea quede pausada, usa get_video_script para leer las escenas, ofr\xE9celas al usuario, y llama approve_video_script con las ediciones. Para dirigir el guion ANTES de generar (sin pausar), usa scriptNotes. Para revisar el \xE1ngulo y decidir duraci\xF3n tras el an\xE1lisis de competencia, pasa `pauseForAngleReview: true` y luego resuelve `angle_review_approval` con `gapDurationSeconds`.', VideoInput.shape, async (input) => {
9582
9738
  try {
9583
9739
  assertVideoPresetDurationCompatibility(input.videoPreset, input.durationSeconds);
9584
9740
  const resolved = resolveVideoPreset(input.videoPreset);
@@ -9655,8 +9811,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
9655
9811
  __name(registerCreativeTools, "registerCreativeTools");
9656
9812
 
9657
9813
  // src/tools/creative-variants.ts
9658
- import { z as z45 } from "zod";
9659
- var LandingSectionKeyEnum = z45.enum([
9814
+ import { z as z46 } from "zod";
9815
+ var LandingSectionKeyEnum = z46.enum([
9660
9816
  "problem_hook",
9661
9817
  "problem_cost",
9662
9818
  "solution_mechanism",
@@ -9696,24 +9852,24 @@ var LandingSectionKeyEnum = z45.enum([
9696
9852
  "lifestyle_fit",
9697
9853
  "join_lifestyle"
9698
9854
  ]);
9699
- var CreativeVariantKindEnum = z45.enum([
9855
+ var CreativeVariantKindEnum = z46.enum([
9700
9856
  "video",
9701
9857
  "image",
9702
9858
  "landing"
9703
9859
  ]);
9704
- var EstimateCreativeVariantInputBase = z45.object({
9705
- sourceTaskId: z45.string().uuid().describe("ID de la tarea original completada."),
9860
+ var EstimateCreativeVariantInputBase = z46.object({
9861
+ sourceTaskId: z46.string().uuid().describe("ID de la tarea original completada."),
9706
9862
  kind: CreativeVariantKindEnum,
9707
9863
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
9708
9864
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
9709
- nVariants: z45.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
9865
+ nVariants: z46.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
9710
9866
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
9711
- instructions: z45.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.")
9867
+ instructions: z46.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.")
9712
9868
  }).strict();
9713
9869
  var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
9714
9870
  if (input.kind === "video" && input.nVariants !== void 0) {
9715
9871
  ctx.addIssue({
9716
- code: z45.ZodIssueCode.custom,
9872
+ code: z46.ZodIssueCode.custom,
9717
9873
  path: [
9718
9874
  "nVariants"
9719
9875
  ],
@@ -9723,7 +9879,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
9723
9879
  if (input.kind !== "video") {
9724
9880
  if (input.videoPreset !== void 0) {
9725
9881
  ctx.addIssue({
9726
- code: z45.ZodIssueCode.custom,
9882
+ code: z46.ZodIssueCode.custom,
9727
9883
  path: [
9728
9884
  "videoPreset"
9729
9885
  ],
@@ -9732,7 +9888,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
9732
9888
  }
9733
9889
  if (input.videoResolution !== void 0) {
9734
9890
  ctx.addIssue({
9735
- code: z45.ZodIssueCode.custom,
9891
+ code: z46.ZodIssueCode.custom,
9736
9892
  path: [
9737
9893
  "videoResolution"
9738
9894
  ],
@@ -9742,7 +9898,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
9742
9898
  }
9743
9899
  if (input.kind !== "image" && input.nVariants !== void 0) {
9744
9900
  ctx.addIssue({
9745
- code: z45.ZodIssueCode.custom,
9901
+ code: z46.ZodIssueCode.custom,
9746
9902
  path: [
9747
9903
  "nVariants"
9748
9904
  ],
@@ -9752,7 +9908,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
9752
9908
  if (input.kind !== "landing") {
9753
9909
  if (input.sectionKey !== void 0) {
9754
9910
  ctx.addIssue({
9755
- code: z45.ZodIssueCode.custom,
9911
+ code: z46.ZodIssueCode.custom,
9756
9912
  path: [
9757
9913
  "sectionKey"
9758
9914
  ],
@@ -9761,7 +9917,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
9761
9917
  }
9762
9918
  if (input.instructions !== void 0) {
9763
9919
  ctx.addIssue({
9764
- code: z45.ZodIssueCode.custom,
9920
+ code: z46.ZodIssueCode.custom,
9765
9921
  path: [
9766
9922
  "instructions"
9767
9923
  ],
@@ -9770,31 +9926,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
9770
9926
  }
9771
9927
  }
9772
9928
  });
9773
- var CreateCreativeVariantInputBase = z45.object({
9774
- sourceTaskId: z45.string().uuid().describe("ID de la tarea original completada."),
9929
+ var CreateCreativeVariantInputBase = z46.object({
9930
+ sourceTaskId: z46.string().uuid().describe("ID de la tarea original completada."),
9775
9931
  kind: CreativeVariantKindEnum,
9776
- confirm: z45.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
9777
- clientRequestId: z45.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."),
9778
- angle: z45.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
9779
- buyerPersona: z45.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
9780
- scriptNotes: z45.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
9781
- creativeDirection: z45.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
9932
+ confirm: z46.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
9933
+ clientRequestId: z46.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."),
9934
+ angle: z46.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
9935
+ buyerPersona: z46.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
9936
+ scriptNotes: z46.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
9937
+ creativeDirection: z46.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
9782
9938
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
9783
9939
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
9784
- generateAiMusic: z45.boolean().optional().describe("Opcional para video."),
9785
- nVariants: z45.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
9940
+ generateAiMusic: z46.boolean().optional().describe("Opcional para video."),
9941
+ nVariants: z46.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
9786
9942
  imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
9787
9943
  creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
9788
9944
  visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
9789
9945
  aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
9790
- styleMix: z45.boolean().optional().describe("Opcional para imagen."),
9946
+ styleMix: z46.boolean().optional().describe("Opcional para imagen."),
9791
9947
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
9792
- instructions: z45.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.")
9948
+ instructions: z46.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.")
9793
9949
  }).strict();
9794
9950
  var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
9795
9951
  if ((input.kind === "video" || input.kind === "image") && !input.angle) {
9796
9952
  ctx.addIssue({
9797
- code: z45.ZodIssueCode.custom,
9953
+ code: z46.ZodIssueCode.custom,
9798
9954
  path: [
9799
9955
  "angle"
9800
9956
  ],
@@ -9803,7 +9959,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
9803
9959
  }
9804
9960
  if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
9805
9961
  ctx.addIssue({
9806
- code: z45.ZodIssueCode.custom,
9962
+ code: z46.ZodIssueCode.custom,
9807
9963
  path: [
9808
9964
  "instructions"
9809
9965
  ],
@@ -9849,7 +10005,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
9849
10005
  for (const field of fieldsByKind[input.kind]) {
9850
10006
  if (input[field] !== void 0) {
9851
10007
  ctx.addIssue({
9852
- code: z45.ZodIssueCode.custom,
10008
+ code: z46.ZodIssueCode.custom,
9853
10009
  path: [
9854
10010
  field
9855
10011
  ],
@@ -9966,27 +10122,27 @@ function registerCreativeVariantTools(server, api) {
9966
10122
  __name(registerCreativeVariantTools, "registerCreativeVariantTools");
9967
10123
 
9968
10124
  // src/tools/script-review.ts
9969
- import { z as z46 } from "zod";
9970
- var GetVideoScriptInput = z46.object({
9971
- taskId: z46.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
9972
- angleKey: z46.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.')
10125
+ import { z as z47 } from "zod";
10126
+ var GetVideoScriptInput = z47.object({
10127
+ taskId: z47.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
10128
+ angleKey: z47.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.')
9973
10129
  }).strict();
9974
10130
  var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
9975
10131
  var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
9976
- var ApproveSceneEdit = z46.object({
9977
- index: z46.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
9978
- dialogue: z46.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
10132
+ var ApproveSceneEdit = z47.object({
10133
+ index: z47.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
10134
+ dialogue: z47.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
9979
10135
  message: "dialogue no puede ser s\xF3lo espacios"
9980
10136
  }).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.")
9981
10137
  }).strict();
9982
- var ApproveVideoScriptInput = z46.object({
9983
- taskId: z46.string().min(1).describe("ID de la tarea."),
9984
- angleKey: z46.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.'),
9985
- scenes: z46.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.")
10138
+ var ApproveVideoScriptInput = z47.object({
10139
+ taskId: z47.string().min(1).describe("ID de la tarea."),
10140
+ angleKey: z47.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.'),
10141
+ scenes: z47.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.")
9986
10142
  }).strict();
9987
- var RegenerateVideoScriptInput = z46.object({
9988
- taskId: z46.string().min(1).describe("ID de la tarea."),
9989
- angleKey: z46.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.'),
10143
+ var RegenerateVideoScriptInput = z47.object({
10144
+ taskId: z47.string().min(1).describe("ID de la tarea."),
10145
+ angleKey: z47.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.'),
9990
10146
  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).')
9991
10147
  }).strict();
9992
10148
  function registerScriptReviewTools(server, api) {
@@ -10026,9 +10182,9 @@ function registerScriptReviewTools(server, api) {
10026
10182
  __name(registerScriptReviewTools, "registerScriptReviewTools");
10027
10183
 
10028
10184
  // src/tools/connections.ts
10029
- import { z as z47 } from "zod";
10185
+ import { z as z48 } from "zod";
10030
10186
  function registerConnectionTools(server, api) {
10031
- server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z47.object({}).shape, async () => {
10187
+ server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z48.object({}).shape, async () => {
10032
10188
  try {
10033
10189
  const r = await api.getJson("/v1/shopify/shops");
10034
10190
  return asText(r);
@@ -10036,10 +10192,10 @@ function registerConnectionTools(server, api) {
10036
10192
  throw translateError(e);
10037
10193
  }
10038
10194
  });
10039
- server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z47.object({
10040
- shopDomain: z47.string().min(3).max(120),
10041
- query: z47.string().trim().max(120).optional(),
10042
- taskId: z47.string().uuid().optional()
10195
+ server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z48.object({
10196
+ shopDomain: z48.string().min(3).max(120),
10197
+ query: z48.string().trim().max(120).optional(),
10198
+ taskId: z48.string().uuid().optional()
10043
10199
  }).shape, async ({ shopDomain, query, taskId }) => {
10044
10200
  try {
10045
10201
  const params = new URLSearchParams({
@@ -10053,7 +10209,7 @@ function registerConnectionTools(server, api) {
10053
10209
  throw translateError(e);
10054
10210
  }
10055
10211
  });
10056
- server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z47.object({}).shape, async () => {
10212
+ server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z48.object({}).shape, async () => {
10057
10213
  try {
10058
10214
  const r = await api.getJson("/v1/facebook/ad-accounts");
10059
10215
  return asText(r);
@@ -10061,7 +10217,7 @@ function registerConnectionTools(server, api) {
10061
10217
  throw translateError(e);
10062
10218
  }
10063
10219
  });
10064
- server.tool("list_tiktok_advertisers", "Lista las cuentas de TikTok Ads conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z47.object({}).shape, async () => {
10220
+ server.tool("list_tiktok_advertisers", "Lista las cuentas de TikTok Ads conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z48.object({}).shape, async () => {
10065
10221
  try {
10066
10222
  const r = await api.getJson("/v1/tiktok/advertisers");
10067
10223
  return asText(r);
@@ -10072,106 +10228,129 @@ function registerConnectionTools(server, api) {
10072
10228
  }
10073
10229
  __name(registerConnectionTools, "registerConnectionTools");
10074
10230
 
10231
+ // src/tools/actors.ts
10232
+ import { z as z49 } from "zod";
10233
+ function registerActorTools(server, api) {
10234
+ server.tool("list_actors", "Lista los personajes guardados del usuario (personas creadas con IA que pueden protagonizar los videos e im\xE1genes). 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.", z49.object({}).shape, async () => {
10235
+ try {
10236
+ const rows = await api.getJson("/v1/actors");
10237
+ const actors = (Array.isArray(rows) ? rows : []).filter((actor) => actor.status === "ready").map(({ id, name, description, gender, voiceId }) => ({
10238
+ id,
10239
+ name,
10240
+ description,
10241
+ gender,
10242
+ voiceId: voiceId ?? null
10243
+ }));
10244
+ return asText({
10245
+ actors
10246
+ });
10247
+ } catch (e) {
10248
+ throw translateError(e);
10249
+ }
10250
+ });
10251
+ }
10252
+ __name(registerActorTools, "registerActorTools");
10253
+
10075
10254
  // src/tools/publish.ts
10076
- import { z as z48 } from "zod";
10077
- var ShopifyInput = z48.object({
10078
- taskId: z48.string().min(1),
10079
- shopDomain: z48.string().min(1),
10255
+ import { z as z50 } from "zod";
10256
+ var ShopifyInput = z50.object({
10257
+ taskId: z50.string().min(1),
10258
+ shopDomain: z50.string().min(1),
10080
10259
  /** Override: number of landing images to include. Default = ALL images the
10081
10260
  * task generated (the MCP fetches /v1/results first to count). Bounded
10082
10261
  * generously here; the backend validates each index against
10083
10262
  * landing.images.length and rejects out-of-range. */
10084
- numLandingImages: z48.number().int().min(1).max(20).optional(),
10085
- variantIndex: z48.number().int().min(0).optional(),
10263
+ numLandingImages: z50.number().int().min(1).max(20).optional(),
10264
+ variantIndex: z50.number().int().min(0).optional(),
10086
10265
  /** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
10087
10266
  * When true, the landing renders variant-picker pills; when false, it ships
10088
10267
  * a single-option product page. Defaults to false. */
10089
- showVariantPicker: z48.boolean().optional(),
10268
+ showVariantPicker: z50.boolean().optional(),
10090
10269
  /** Default `auto` updates the product previously published for this task and
10091
10270
  * shop. Use `existing` with targetProductId to choose another product, or
10092
10271
  * `new` only when the user explicitly wants a duplicate/new listing. */
10093
- publicationMode: z48.enum([
10272
+ publicationMode: z50.enum([
10094
10273
  "auto",
10095
10274
  "existing",
10096
10275
  "new"
10097
10276
  ]).optional(),
10098
10277
  /** Shopify product GID, numeric id, or admin product URL. Required when
10099
10278
  * publicationMode is `existing`. */
10100
- targetProductId: z48.string().trim().min(1).max(300).optional()
10279
+ targetProductId: z50.string().trim().min(1).max(300).optional()
10101
10280
  });
10102
- var McpCampaignConfig = z48.object({
10281
+ var McpCampaignConfig = z50.object({
10103
10282
  /** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
10104
10283
  * converts to the account currency server-side. Do NOT pre-convert: on a COP
10105
10284
  * account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
10106
- dailyBudgetUsd: z48.number().positive().optional(),
10285
+ dailyBudgetUsd: z50.number().positive().optional(),
10107
10286
  /** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
10108
10287
  * ad set). TikTok takes only the first entry. */
10109
- targetCountries: z48.array(z48.string().length(2)).max(1).optional(),
10110
- campaignName: z48.string().trim().min(1).max(100).optional()
10288
+ targetCountries: z50.array(z50.string().length(2)).max(1).optional(),
10289
+ campaignName: z50.string().trim().min(1).max(100).optional()
10111
10290
  });
10112
10291
  var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
10113
- var MetaInput = z48.object({
10114
- taskId: z48.string().min(1),
10292
+ var MetaInput = z50.object({
10293
+ taskId: z50.string().min(1),
10115
10294
  adAccountId: McpAdAccountId,
10116
- pixelId: z48.string().min(1).optional(),
10117
- landingUrl: z48.string().url().optional(),
10295
+ pixelId: z50.string().min(1).optional(),
10296
+ landingUrl: z50.string().url().optional(),
10118
10297
  /** Select which angles to publish by 0-based index. Omit to publish all
10119
10298
  * successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
10120
- angleIndexes: z48.array(z48.number().int().nonnegative()).optional(),
10299
+ angleIndexes: z50.array(z50.number().int().nonnegative()).optional(),
10121
10300
  campaignConfig: McpCampaignConfig.optional(),
10122
10301
  /** Override the ad's primary text (body copy). If omitted, ProductMaker
10123
10302
  * uses the auto-generated copy attached to the task. Capped at 500 chars
10124
10303
  * (Meta's primary-text limit). */
10125
- primaryText: z48.string().trim().min(1).max(500).optional(),
10304
+ primaryText: z50.string().trim().min(1).max(500).optional(),
10126
10305
  /** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
10127
10306
  * (wa.me) instead of a landing page. */
10128
- phoneNumber: z48.string().min(8).max(32).optional()
10307
+ phoneNumber: z50.string().min(8).max(32).optional()
10129
10308
  });
10130
- var GroupedTaskSource = z48.object({
10131
- type: z48.literal("task"),
10132
- taskId: z48.string().min(1),
10133
- angleIndex: z48.number().int().min(0).optional(),
10134
- videoVariant: z48.enum([
10309
+ var GroupedTaskSource = z50.object({
10310
+ type: z50.literal("task"),
10311
+ taskId: z50.string().min(1),
10312
+ angleIndex: z50.number().int().min(0).optional(),
10313
+ videoVariant: z50.enum([
10135
10314
  "with_subtitles",
10136
10315
  "without_subtitles"
10137
10316
  ]).optional(),
10138
- imageVariantIndex: z48.number().int().min(0).optional()
10139
- });
10140
- var GroupedMediaSource = z48.object({
10141
- type: z48.literal("media"),
10142
- url: z48.string().url().optional(),
10143
- path: z48.string().min(1).optional(),
10144
- thumbnailUrl: z48.string().url().optional(),
10145
- thumbnailPath: z48.string().min(1).optional()
10317
+ imageVariantIndex: z50.number().int().min(0).optional()
10318
+ });
10319
+ var GroupedMediaSource = z50.object({
10320
+ type: z50.literal("media"),
10321
+ url: z50.string().url().optional(),
10322
+ path: z50.string().min(1).optional(),
10323
+ thumbnailUrl: z50.string().url().optional(),
10324
+ thumbnailPath: z50.string().min(1).optional()
10146
10325
  }).refine((value) => Boolean(value.url) !== Boolean(value.path), {
10147
10326
  message: "media source needs exactly one of `url` or `path`"
10148
10327
  }).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
10149
10328
  message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
10150
10329
  });
10151
- var GroupedCreative = z48.object({
10152
- type: z48.enum([
10330
+ var GroupedCreative = z50.object({
10331
+ type: z50.enum([
10153
10332
  "image",
10154
10333
  "video"
10155
10334
  ]),
10156
- source: z48.union([
10335
+ source: z50.union([
10157
10336
  GroupedTaskSource,
10158
10337
  GroupedMediaSource
10159
10338
  ]),
10160
- title: z48.string().max(120).optional(),
10161
- description: z48.string().max(500).optional(),
10339
+ title: z50.string().max(120).optional(),
10340
+ description: z50.string().max(500).optional(),
10162
10341
  cta: MetaCtaType.optional(),
10163
- creativeId: z48.string().min(1).optional()
10342
+ creativeId: z50.string().min(1).optional()
10164
10343
  });
10165
- var GroupedCampaignConfig = z48.object({
10344
+ var GroupedCampaignConfig = z50.object({
10166
10345
  campaignType: MetaCampaignType.default("sales"),
10167
10346
  optimizationMode: MetaOptimizationMode.default("advantage_plus"),
10168
10347
  budgetMode: MetaBudgetMode.default("campaign"),
10169
- campaignDailyBudget: z48.number().int().positive().optional(),
10170
- adSets: z48.array(z48.object({
10171
- tempId: z48.string().min(1).max(80),
10172
- name: z48.string().min(1).max(100),
10173
- dailyBudget: z48.number().int().positive().optional(),
10174
- creativeIds: z48.array(z48.string().min(1)).min(1)
10348
+ campaignDailyBudget: z50.number().int().positive().optional(),
10349
+ adSets: z50.array(z50.object({
10350
+ tempId: z50.string().min(1).max(80),
10351
+ name: z50.string().min(1).max(100),
10352
+ dailyBudget: z50.number().int().positive().optional(),
10353
+ creativeIds: z50.array(z50.string().min(1)).min(1)
10175
10354
  })).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
10176
10355
  /**
10177
10356
  * Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
@@ -10179,22 +10358,22 @@ var GroupedCampaignConfig = z48.object({
10179
10358
  * opaque `invalid_literal` and made the WhatsApp destination — supported by
10180
10359
  * the API since #849 — unreachable from this tool.
10181
10360
  */
10182
- destination: z48.discriminatedUnion("type", [
10183
- z48.object({
10184
- type: z48.literal("landing"),
10185
- landingUrl: z48.string().url()
10361
+ destination: z50.discriminatedUnion("type", [
10362
+ z50.object({
10363
+ type: z50.literal("landing"),
10364
+ landingUrl: z50.string().url()
10186
10365
  }),
10187
- z48.object({
10188
- type: z48.literal("whatsapp"),
10189
- phoneNumber: z48.string().min(8).max(32)
10366
+ z50.object({
10367
+ type: z50.literal("whatsapp"),
10368
+ phoneNumber: z50.string().min(8).max(32)
10190
10369
  })
10191
10370
  ]),
10192
- saveAsDefault: z48.boolean().default(false),
10371
+ saveAsDefault: z50.boolean().default(false),
10193
10372
  budgetPolicy: MetaBudgetPolicy.optional()
10194
10373
  }).superRefine((value, ctx) => {
10195
10374
  if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
10196
10375
  ctx.addIssue({
10197
- code: z48.ZodIssueCode.custom,
10376
+ code: z50.ZodIssueCode.custom,
10198
10377
  path: [
10199
10378
  "campaignDailyBudget"
10200
10379
  ],
@@ -10205,7 +10384,7 @@ var GroupedCampaignConfig = z48.object({
10205
10384
  value.adSets.forEach((adSet, index) => {
10206
10385
  if (adSet.dailyBudget === void 0) {
10207
10386
  ctx.addIssue({
10208
- code: z48.ZodIssueCode.custom,
10387
+ code: z50.ZodIssueCode.custom,
10209
10388
  path: [
10210
10389
  "adSets",
10211
10390
  index,
@@ -10217,22 +10396,22 @@ var GroupedCampaignConfig = z48.object({
10217
10396
  });
10218
10397
  }
10219
10398
  });
10220
- var PublishCreativesToMetaInputBase = z48.object({
10221
- creatives: z48.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
10399
+ var PublishCreativesToMetaInputBase = z50.object({
10400
+ creatives: z50.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
10222
10401
  adAccountId: McpAdAccountId,
10223
- pixelId: z48.string().min(1).optional(),
10402
+ pixelId: z50.string().min(1).optional(),
10224
10403
  /** Required unless `campaignConfig.destination` is whatsapp — see the
10225
10404
  * superRefine below. Mirrors the API's `UploadToMetaRequest`. */
10226
- landingUrl: z48.string().url().optional(),
10227
- targetCountry: z48.string().length(2),
10228
- campaignName: z48.string().trim().min(1).max(100),
10405
+ landingUrl: z50.string().url().optional(),
10406
+ targetCountry: z50.string().length(2),
10407
+ campaignName: z50.string().trim().min(1).max(100),
10229
10408
  campaignConfig: GroupedCampaignConfig.optional()
10230
10409
  });
10231
10410
  var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
10232
10411
  const destination = value.campaignConfig?.destination;
10233
10412
  if (destination?.type !== "whatsapp" && !value.landingUrl) {
10234
10413
  ctx.addIssue({
10235
- code: z48.ZodIssueCode.custom,
10414
+ code: z50.ZodIssueCode.custom,
10236
10415
  path: [
10237
10416
  "landingUrl"
10238
10417
  ],
@@ -10241,7 +10420,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
10241
10420
  }
10242
10421
  if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
10243
10422
  ctx.addIssue({
10244
- code: z48.ZodIssueCode.custom,
10423
+ code: z50.ZodIssueCode.custom,
10245
10424
  path: [
10246
10425
  "campaignConfig",
10247
10426
  "destination",
@@ -10251,12 +10430,12 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
10251
10430
  });
10252
10431
  }
10253
10432
  });
10254
- var PresignUploadResponseSchema = z48.object({
10255
- uploadUrl: z48.string().url().refine((value) => {
10433
+ var PresignUploadResponseSchema = z50.object({
10434
+ uploadUrl: z50.string().url().refine((value) => {
10256
10435
  const url = new URL(value);
10257
10436
  return !url.username && !url.password && url.protocol === "https:";
10258
10437
  }, "presigned upload URL must be HTTPS without embedded credentials"),
10259
- publicUrl: z48.string().url().refine((value) => {
10438
+ publicUrl: z50.string().url().refine((value) => {
10260
10439
  const url = new URL(value);
10261
10440
  return !url.username && !url.password;
10262
10441
  }, "public media URL must not contain embedded credentials")
@@ -10281,16 +10460,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
10281
10460
  return uploadResolvedMedia(api, media);
10282
10461
  }
10283
10462
  __name(resolveAndUploadMedia, "resolveAndUploadMedia");
10284
- var TikTokInput = z48.object({
10285
- taskId: z48.string().min(1),
10286
- advertiserId: z48.string().min(1),
10287
- pixelId: z48.string().min(1).optional(),
10288
- landingUrl: z48.string().url().optional(),
10289
- angleIndexes: z48.array(z48.number().int().nonnegative()).optional(),
10463
+ var TikTokInput = z50.object({
10464
+ taskId: z50.string().min(1),
10465
+ advertiserId: z50.string().min(1),
10466
+ pixelId: z50.string().min(1).optional(),
10467
+ landingUrl: z50.string().url().optional(),
10468
+ angleIndexes: z50.array(z50.number().int().nonnegative()).optional(),
10290
10469
  campaignConfig: McpCampaignConfig.optional(),
10291
10470
  /** Override the ad's description (TikTok's body copy). If omitted,
10292
10471
  * ProductMaker uses the auto-generated copy attached to the task. */
10293
- primaryText: z48.string().trim().min(1).max(500).optional()
10472
+ primaryText: z50.string().trim().min(1).max(500).optional()
10294
10473
  });
10295
10474
  function registerPublishTools(server, api, opts = {}) {
10296
10475
  const transport = opts.transport ?? "http";
@@ -10501,13 +10680,13 @@ function toTikTokCampaignConfig(cfg) {
10501
10680
  __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
10502
10681
 
10503
10682
  // src/tools/campaigns.ts
10504
- import { z as z49 } from "zod";
10505
- var MetaStatusInput = z49.object({
10506
- campaignId: z49.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
10683
+ import { z as z51 } from "zod";
10684
+ var MetaStatusInput = z51.object({
10685
+ campaignId: z51.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
10507
10686
  });
10508
- var TikTokStatusInput = z49.object({
10509
- campaignId: z49.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
10510
- advertiserId: z49.string().min(1).describe("The same advertiserId used to publish the campaign.")
10687
+ var TikTokStatusInput = z51.object({
10688
+ campaignId: z51.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
10689
+ advertiserId: z51.string().min(1).describe("The same advertiserId used to publish the campaign.")
10511
10690
  });
10512
10691
  function registerCampaignTools(server, api) {
10513
10692
  server.tool("get_meta_campaign_status", "Consulta el estado actual + m\xE9tricas lifetime (spend, impressions, clicks, reach) de una campa\xF1a Meta publicada. Llama esto DESPU\xC9S de `publish_to_meta` para confirmar que la campa\xF1a se cre\xF3 correctamente y para reportar performance al usuario. Es read-only \u2014 no modifica nada en Meta Ads Manager.", MetaStatusInput.shape, async (input) => {
@@ -10604,10 +10783,10 @@ function renderTikTokStatusMarkdown(r) {
10604
10783
  __name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
10605
10784
 
10606
10785
  // ../../packages/product-economics/src/product-costing.ts
10607
- import { z as z51 } from "zod";
10786
+ import { z as z53 } from "zod";
10608
10787
 
10609
10788
  // ../../packages/product-economics/src/product-costing-countries.ts
10610
- import { z as z50 } from "zod";
10789
+ import { z as z52 } from "zod";
10611
10790
 
10612
10791
  // ../../packages/product-economics/src/product-costing-shipping-references.json
10613
10792
  var product_costing_shipping_references_default = {
@@ -10671,21 +10850,21 @@ var product_costing_shipping_references_default = {
10671
10850
  };
10672
10851
 
10673
10852
  // ../../packages/product-economics/src/product-costing-countries.ts
10674
- var ShippingReferenceSchema = z50.object({
10675
- reference: z50.number().positive(),
10676
- min: z50.number().nonnegative(),
10677
- max: z50.number().positive(),
10678
- updatedAt: z50.string().date(),
10679
- confidence: z50.enum([
10853
+ var ShippingReferenceSchema = z52.object({
10854
+ reference: z52.number().positive(),
10855
+ min: z52.number().nonnegative(),
10856
+ max: z52.number().positive(),
10857
+ updatedAt: z52.string().date(),
10858
+ confidence: z52.enum([
10680
10859
  "high",
10681
10860
  "medium",
10682
10861
  "medium-low"
10683
10862
  ]),
10684
- sources: z50.array(z50.string().url().startsWith("https://")).min(1).max(3)
10863
+ sources: z52.array(z52.string().url().startsWith("https://")).min(1).max(3)
10685
10864
  }).superRefine(({ reference, min, max }, context) => {
10686
10865
  if (min > reference || reference > max) {
10687
10866
  context.addIssue({
10688
- code: z50.ZodIssueCode.custom,
10867
+ code: z52.ZodIssueCode.custom,
10689
10868
  message: "shipping_reference_outside_range",
10690
10869
  path: [
10691
10870
  "reference"
@@ -10693,7 +10872,7 @@ var ShippingReferenceSchema = z50.object({
10693
10872
  });
10694
10873
  }
10695
10874
  });
10696
- var SHIPPING_REFERENCES = z50.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
10875
+ var SHIPPING_REFERENCES = z52.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
10697
10876
  var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
10698
10877
  var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
10699
10878
  "MX",
@@ -10875,18 +11054,18 @@ function getProductCostingCountryName(country) {
10875
11054
  __name(getProductCostingCountryName, "getProductCostingCountryName");
10876
11055
 
10877
11056
  // ../../packages/product-economics/src/product-costing.ts
10878
- var ProductCostingInputSchema = z51.object({
10879
- country: z51.string().refine(isProductCostingCountryCode, "unsupported_country"),
10880
- supplierCost: z51.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
10881
- shippingCost: z51.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
10882
- returnRate: z51.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
10883
- cancellationRate: z51.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
10884
- adsManagerCpa: z51.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
10885
- desiredMargin: z51.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
11057
+ var ProductCostingInputSchema = z53.object({
11058
+ country: z53.string().refine(isProductCostingCountryCode, "unsupported_country"),
11059
+ supplierCost: z53.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
11060
+ shippingCost: z53.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
11061
+ returnRate: z53.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
11062
+ cancellationRate: z53.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
11063
+ adsManagerCpa: z53.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
11064
+ desiredMargin: z53.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
10886
11065
  }).superRefine(({ returnRate, cancellationRate }, context) => {
10887
11066
  if (returnRate + cancellationRate >= 1) {
10888
11067
  context.addIssue({
10889
- code: z51.ZodIssueCode.custom,
11068
+ code: z53.ZodIssueCode.custom,
10890
11069
  message: "delivery_rate_positive",
10891
11070
  path: [
10892
11071
  "cancellationRate"
@@ -10953,14 +11132,14 @@ function calculateProductCosting(rawInput) {
10953
11132
  __name(calculateProductCosting, "calculateProductCosting");
10954
11133
 
10955
11134
  // src/tools/product-costing.ts
10956
- import { z as z52 } from "zod";
10957
- var optionalAmount = /* @__PURE__ */ __name((description) => z52.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
10958
- var optionalPercent = /* @__PURE__ */ __name((description) => z52.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
10959
- var ProductCostingToolInput = z52.object({
10960
- country: z52.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
11135
+ import { z as z54 } from "zod";
11136
+ var optionalAmount = /* @__PURE__ */ __name((description) => z54.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
11137
+ var optionalPercent = /* @__PURE__ */ __name((description) => z54.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
11138
+ var ProductCostingToolInput = z54.object({
11139
+ country: z54.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
10961
11140
  message: "Pa\xEDs no soportado por ProductMaker."
10962
11141
  }).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
10963
- supplierCost: z52.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."),
11142
+ supplierCost: z54.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."),
10964
11143
  supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
10965
11144
  shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
10966
11145
  shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
@@ -11092,7 +11271,7 @@ function formatPercent(value) {
11092
11271
  __name(formatPercent, "formatPercent");
11093
11272
 
11094
11273
  // src/tools/product-research.ts
11095
- import { z as z53 } from "zod";
11274
+ import { z as z55 } from "zod";
11096
11275
 
11097
11276
  // src/tools/product-research-idempotency.ts
11098
11277
  import { createHash as createHash2 } from "crypto";
@@ -11208,17 +11387,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
11208
11387
  ]);
11209
11388
  var POLL_INTERVAL_MS = 5e3;
11210
11389
  var MAX_WAIT_SECONDS = 120;
11211
- var FindInput = z53.object({
11390
+ var FindInput = z55.object({
11212
11391
  targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
11213
11392
  categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
11214
- confirmed: z53.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
11215
- waitSeconds: z53.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
11393
+ confirmed: z55.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
11394
+ waitSeconds: z55.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
11216
11395
  });
11217
- var GetInput = z53.object({
11218
- runId: z53.string().uuid().describe("ID del run devuelto por find_winning_products.")
11396
+ var GetInput = z55.object({
11397
+ runId: z55.string().uuid().describe("ID del run devuelto por find_winning_products.")
11219
11398
  });
11220
- var ListInput = z53.object({
11221
- limit: z53.number().int().min(1).max(20).default(10)
11399
+ var ListInput = z55.object({
11400
+ limit: z55.number().int().min(1).max(20).default(10)
11222
11401
  });
11223
11402
  function textResult(text) {
11224
11403
  return {
@@ -11310,14 +11489,14 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
11310
11489
  __name(registerProductResearchTools, "registerProductResearchTools");
11311
11490
 
11312
11491
  // src/tools/dropshipping-projects.ts
11313
- import { z as z54 } from "zod";
11314
- var ProjectId = z54.string().uuid();
11315
- var DropshippingProjectStatus = z54.enum([
11492
+ import { z as z56 } from "zod";
11493
+ var ProjectId = z56.string().uuid();
11494
+ var DropshippingProjectStatus = z56.enum([
11316
11495
  "active",
11317
11496
  "completed",
11318
11497
  "archived"
11319
11498
  ]);
11320
- var DropshippingProjectPhase = z54.enum([
11499
+ var DropshippingProjectPhase = z56.enum([
11321
11500
  "research",
11322
11501
  "costing",
11323
11502
  "media",
@@ -11326,21 +11505,21 @@ var DropshippingProjectPhase = z54.enum([
11326
11505
  "advertising",
11327
11506
  "complete"
11328
11507
  ]);
11329
- var DropshippingRewardMilestone = z54.enum([
11508
+ var DropshippingRewardMilestone = z56.enum([
11330
11509
  "product_selected",
11331
11510
  "costing_saved",
11332
11511
  "product_media_ready",
11333
11512
  "creatives_completed",
11334
11513
  "campaign_created"
11335
11514
  ]);
11336
- var TargetCountry = z54.enum([
11515
+ var TargetCountry = z56.enum([
11337
11516
  "CO",
11338
11517
  "MX",
11339
11518
  "PE",
11340
11519
  "EC",
11341
11520
  "CL"
11342
11521
  ]);
11343
- var ResearchCategory = z54.enum([
11522
+ var ResearchCategory = z56.enum([
11344
11523
  "auto",
11345
11524
  "automotive",
11346
11525
  "kitchen",
@@ -11355,7 +11534,7 @@ var ResearchCategory = z54.enum([
11355
11534
  "garden",
11356
11535
  "cleaning"
11357
11536
  ]);
11358
- var HttpUrl3 = z54.string().url().max(2e3).refine((value) => {
11537
+ var HttpUrl3 = z56.string().url().max(2e3).refine((value) => {
11359
11538
  try {
11360
11539
  return [
11361
11540
  "http:",
@@ -11365,46 +11544,46 @@ var HttpUrl3 = z54.string().url().max(2e3).refine((value) => {
11365
11544
  return false;
11366
11545
  }
11367
11546
  }, "URL must use HTTP(S)");
11368
- var ResearchCandidateOrigin = z54.object({
11369
- type: z54.literal("research_candidate"),
11547
+ var ResearchCandidateOrigin = z56.object({
11548
+ type: z56.literal("research_candidate"),
11370
11549
  runId: ProjectId,
11371
- candidateSourceId: z54.string().min(1).max(160)
11550
+ candidateSourceId: z56.string().min(1).max(160)
11372
11551
  });
11373
- var TaskOrigin = z54.object({
11374
- type: z54.literal("task"),
11552
+ var TaskOrigin = z56.object({
11553
+ type: z56.literal("task"),
11375
11554
  taskId: ProjectId
11376
11555
  });
11377
- var ManualOrigin = z54.object({
11378
- type: z54.literal("manual"),
11556
+ var ManualOrigin = z56.object({
11557
+ type: z56.literal("manual"),
11379
11558
  clientRequestId: ProjectId,
11380
- country: z54.string().min(2).max(3).optional()
11559
+ country: z56.string().min(2).max(3).optional()
11381
11560
  });
11382
- var StartProjectInput = z54.object({
11383
- origin: z54.discriminatedUnion("type", [
11561
+ var StartProjectInput = z56.object({
11562
+ origin: z56.discriminatedUnion("type", [
11384
11563
  ResearchCandidateOrigin,
11385
11564
  TaskOrigin,
11386
11565
  ManualOrigin
11387
11566
  ]),
11388
- productName: z54.string().trim().min(1).max(240).optional(),
11567
+ productName: z56.string().trim().min(1).max(240).optional(),
11389
11568
  productUrl: HttpUrl3.optional(),
11390
- country: z54.string().min(2).max(3).optional(),
11391
- languageCode: z54.string().min(2).max(16).optional()
11569
+ country: z56.string().min(2).max(3).optional(),
11570
+ languageCode: z56.string().min(2).max(16).optional()
11392
11571
  }).strict();
11393
- var CostProjectInput = z54.object({
11572
+ var CostProjectInput = z56.object({
11394
11573
  projectId: ProjectId,
11395
- version: z54.number().int().positive(),
11396
- country: z54.string().trim().min(2).max(3),
11397
- supplierCost: z54.number().finite().positive(),
11398
- shippingCost: z54.number().finite().nonnegative(),
11399
- returnRate: z54.number().finite().min(0).lt(1),
11400
- cancellationRate: z54.number().finite().min(0).lt(1),
11401
- adsManagerCpa: z54.number().finite().nonnegative(),
11402
- desiredMargin: z54.number().finite().min(0).lt(1)
11403
- });
11404
- var GuideInput = z54.object({
11574
+ version: z56.number().int().positive(),
11575
+ country: z56.string().trim().min(2).max(3),
11576
+ supplierCost: z56.number().finite().positive(),
11577
+ shippingCost: z56.number().finite().nonnegative(),
11578
+ returnRate: z56.number().finite().min(0).lt(1),
11579
+ cancellationRate: z56.number().finite().min(0).lt(1),
11580
+ adsManagerCpa: z56.number().finite().nonnegative(),
11581
+ desiredMargin: z56.number().finite().min(0).lt(1)
11582
+ });
11583
+ var GuideInput = z56.object({
11405
11584
  projectId: ProjectId.optional()
11406
11585
  }).strict();
11407
- var DropshippingProjectNextAction = z54.discriminatedUnion("kind", [
11586
+ var DropshippingProjectNextAction = z56.discriminatedUnion("kind", [
11408
11587
  "cost_product",
11409
11588
  "provide_product_media",
11410
11589
  "generate_creatives",
@@ -11418,78 +11597,78 @@ var DropshippingProjectNextAction = z54.discriminatedUnion("kind", [
11418
11597
  "create_meta_campaign",
11419
11598
  "repair_meta_campaign",
11420
11599
  "review_paused_campaign"
11421
- ].map((kind) => z54.object({
11422
- kind: z54.literal(kind),
11423
- href: z54.string().min(1)
11600
+ ].map((kind) => z56.object({
11601
+ kind: z56.literal(kind),
11602
+ href: z56.string().min(1)
11424
11603
  })));
11425
- var CostingInputSchema = z54.object({
11426
- country: z54.string().min(2).max(3),
11427
- supplierCost: z54.number().finite().positive(),
11428
- shippingCost: z54.number().finite().nonnegative(),
11429
- returnRate: z54.number().finite().min(0).lt(1),
11430
- cancellationRate: z54.number().finite().min(0).lt(1),
11431
- adsManagerCpa: z54.number().finite().nonnegative(),
11432
- desiredMargin: z54.number().finite().min(0).lt(1)
11604
+ var CostingInputSchema = z56.object({
11605
+ country: z56.string().min(2).max(3),
11606
+ supplierCost: z56.number().finite().positive(),
11607
+ shippingCost: z56.number().finite().nonnegative(),
11608
+ returnRate: z56.number().finite().min(0).lt(1),
11609
+ cancellationRate: z56.number().finite().min(0).lt(1),
11610
+ adsManagerCpa: z56.number().finite().nonnegative(),
11611
+ desiredMargin: z56.number().finite().min(0).lt(1)
11433
11612
  }).superRefine(({ returnRate, cancellationRate }, context) => {
11434
11613
  if (returnRate + cancellationRate >= 1) context.addIssue({
11435
- code: z54.ZodIssueCode.custom,
11614
+ code: z56.ZodIssueCode.custom,
11436
11615
  path: [
11437
11616
  "cancellationRate"
11438
11617
  ]
11439
11618
  });
11440
11619
  });
11441
- var CostingResultSchema = z54.object({
11442
- country: z54.string().min(2).max(3),
11443
- deliveryRate: z54.number().finite(),
11444
- adjustedShippingCost: z54.number().finite(),
11445
- adjustedCpa: z54.number().finite(),
11446
- totalCost: z54.number().finite(),
11447
- suggestedPrice: z54.number().finite(),
11448
- baseCommercialPrice: z54.number().finite(),
11449
- minimumSafePrice: z54.number().finite(),
11450
- commercialPrice: z54.number().finite(),
11451
- comparisonPrice: z54.number().finite(),
11452
- estimatedProfit: z54.number().finite(),
11453
- actualMargin: z54.number().finite(),
11454
- breakEvenAdsManagerCpa: z54.number().finite(),
11455
- wasPriceAutoAdjusted: z54.boolean(),
11456
- profitabilityStatus: z54.literal("healthy")
11457
- });
11458
- var RewardProgressSchema = z54.object({
11459
- campaignKey: z54.literal("dropshipping-launch-v1"),
11460
- totalCredits: z54.number().int().positive(),
11461
- earnedCredits: z54.number().int().nonnegative(),
11462
- milestones: z54.array(z54.object({
11620
+ var CostingResultSchema = z56.object({
11621
+ country: z56.string().min(2).max(3),
11622
+ deliveryRate: z56.number().finite(),
11623
+ adjustedShippingCost: z56.number().finite(),
11624
+ adjustedCpa: z56.number().finite(),
11625
+ totalCost: z56.number().finite(),
11626
+ suggestedPrice: z56.number().finite(),
11627
+ baseCommercialPrice: z56.number().finite(),
11628
+ minimumSafePrice: z56.number().finite(),
11629
+ commercialPrice: z56.number().finite(),
11630
+ comparisonPrice: z56.number().finite(),
11631
+ estimatedProfit: z56.number().finite(),
11632
+ actualMargin: z56.number().finite(),
11633
+ breakEvenAdsManagerCpa: z56.number().finite(),
11634
+ wasPriceAutoAdjusted: z56.boolean(),
11635
+ profitabilityStatus: z56.literal("healthy")
11636
+ });
11637
+ var RewardProgressSchema = z56.object({
11638
+ campaignKey: z56.literal("dropshipping-launch-v1"),
11639
+ totalCredits: z56.number().int().positive(),
11640
+ earnedCredits: z56.number().int().nonnegative(),
11641
+ milestones: z56.array(z56.object({
11463
11642
  milestone: DropshippingRewardMilestone,
11464
- credits: z54.number().int().positive(),
11465
- available: z54.boolean().optional(),
11466
- earned: z54.boolean(),
11643
+ credits: z56.number().int().positive(),
11644
+ available: z56.boolean().optional(),
11645
+ earned: z56.boolean(),
11467
11646
  grantId: ProjectId.nullable(),
11468
- grantedAt: z54.string().datetime().nullable()
11647
+ grantedAt: z56.string().datetime().nullable()
11469
11648
  })).length(DropshippingRewardMilestone.options.length)
11470
11649
  });
11471
- var ProjectResponseSchema = z54.object({
11650
+ var ProjectResponseSchema = z56.object({
11472
11651
  id: ProjectId,
11473
11652
  taskId: ProjectId.nullable(),
11474
11653
  status: DropshippingProjectStatus,
11475
- originKey: z54.string().min(1),
11654
+ originKey: z56.string().min(1),
11476
11655
  researchRunId: ProjectId.nullable(),
11477
- candidateSourceId: z54.string().nullable(),
11478
- productName: z54.string().nullable(),
11656
+ candidateSourceId: z56.string().nullable(),
11657
+ productName: z56.string().nullable(),
11479
11658
  productUrl: HttpUrl3.nullable(),
11480
11659
  referenceImageUrl: HttpUrl3.nullable(),
11481
- targetCountry: z54.string().nullable(),
11482
- languageCode: z54.string().nullable(),
11483
- hasReferenceImage: z54.boolean(),
11660
+ targetCountry: z56.string().nullable(),
11661
+ languageCode: z56.string().nullable(),
11662
+ hasReferenceImage: z56.boolean(),
11484
11663
  costingInput: CostingInputSchema.nullable(),
11485
11664
  costingResult: CostingResultSchema.nullable(),
11486
- costingVersion: z54.number().int().positive().nullable(),
11487
- version: z54.number().int().positive(),
11665
+ costingVersion: z56.number().int().positive().nullable(),
11666
+ version: z56.number().int().positive(),
11488
11667
  phase: DropshippingProjectPhase,
11489
11668
  nextAction: DropshippingProjectNextAction.nullable(),
11490
11669
  rewards: RewardProgressSchema,
11491
- createdAt: z54.string().datetime(),
11492
- updatedAt: z54.string().datetime()
11670
+ createdAt: z56.string().datetime(),
11671
+ updatedAt: z56.string().datetime()
11493
11672
  }).passthrough();
11494
11673
  function projectText(raw) {
11495
11674
  const project = ProjectResponseSchema.parse(raw);
@@ -11587,6 +11766,7 @@ function createServer(opts) {
11587
11766
  registerCreativeVariantTools(server, api);
11588
11767
  registerScriptReviewTools(server, api);
11589
11768
  registerConnectionTools(server, api);
11769
+ registerActorTools(server, api);
11590
11770
  registerPublishTools(server, api, {
11591
11771
  transport
11592
11772
  });