@productmaker/mcp 11.2.2 → 11.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +12 -0
  2. package/dist/http.js +1359 -472
  3. package/dist/stdio.js +1359 -472
  4. package/package.json +1 -1
package/dist/http.js CHANGED
@@ -15684,6 +15684,54 @@ var CAPABILITY_MODEL_GUIDANCE = {
15684
15684
  "es trabajo pagado: se cobra el m\xE1ximo del rango de su duraci\xF3n (m\xE1s cada versi\xF3n extra del gancho) y al terminar se devuelve lo que no use; requiere confirmaci\xF3n expl\xEDcita; tarda varios minutos"
15685
15685
  ]
15686
15686
  },
15687
+ create_brand: {
15688
+ summary: "Analiza la marca de un negocio de servicios o marca personal desde su Instagram, Facebook, TikTok o web.",
15689
+ useWhen: [
15690
+ "toma mi Instagram y hazme el an\xE1lisis de marca",
15691
+ "la persona comparte el Instagram o la web de su negocio de servicios y quiere su marca, landing o video"
15692
+ ],
15693
+ avoidWhen: [
15694
+ "la persona habla de un producto de dropshipping, un proveedor o un enlace de AliExpress o Dropi"
15695
+ ],
15696
+ defaults: [
15697
+ "reutiliza una marca existente de la misma direcci\xF3n antes de pagar otro an\xE1lisis"
15698
+ ],
15699
+ disclosures: [
15700
+ "es trabajo pagado: muestra el precio y requiere confirmaci\xF3n expl\xEDcita"
15701
+ ]
15702
+ },
15703
+ create_service_landing: {
15704
+ summary: "Crea y publica la landing page de un negocio de servicios a partir de su marca.",
15705
+ useWhen: [
15706
+ "hazme la landing page de mi negocio",
15707
+ "la persona tiene una marca de servicios y quiere su p\xE1gina para recibir clientes"
15708
+ ],
15709
+ avoidWhen: [
15710
+ "la persona quiere una landing de un producto de dropshipping o de una tarea de producto"
15711
+ ],
15712
+ defaults: [
15713
+ "pregunta una sola vez el contacto, la zona y los servicios con precio que falten"
15714
+ ],
15715
+ disclosures: [
15716
+ "es trabajo pagado: muestra el precio total y requiere confirmaci\xF3n expl\xEDcita; publicar no cobra"
15717
+ ]
15718
+ },
15719
+ create_service_images: {
15720
+ summary: "Crea im\xE1genes para anuncios de un negocio de servicios con su marca, sin foto de producto.",
15721
+ useWhen: [
15722
+ "hazme im\xE1genes para anunciar mi servicio",
15723
+ "la persona quiere im\xE1genes publicitarias de su negocio de servicios"
15724
+ ],
15725
+ avoidWhen: [
15726
+ "la persona quiere im\xE1genes de un producto de dropshipping con sus fotos"
15727
+ ],
15728
+ defaults: [
15729
+ "una imagen 4:5 de calidad est\xE1ndar con la marca"
15730
+ ],
15731
+ disclosures: [
15732
+ "es trabajo pagado: muestra el precio y requiere confirmaci\xF3n expl\xEDcita"
15733
+ ]
15734
+ },
15687
15735
  edit_image_creative: {
15688
15736
  summary: "Corrige UNA imagen ya generada de una tarea de creativos, sin regenerar el set completo.",
15689
15737
  useWhen: [
@@ -18777,6 +18825,92 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
18777
18825
  "task",
18778
18826
  "video"
18779
18827
  ], MCP_ONLY_CHANNEL_EXPOSURE),
18828
+ // MCP-only service journey (2026-10-11): brand analysis → service landing →
18829
+ // create_animated_video. WhatsApp has no adapter for either, so both are
18830
+ // declared unavailable there and its parity gate is unchanged.
18831
+ defineCapability("create_brand", "7f5680574f38f1005d162c2a2a3805be82896508793ebcdd5491b8459c803158", "credit_consuming", "creative", [
18832
+ "credit_consuming",
18833
+ "state_change"
18834
+ ], [
18835
+ slot("brand_action", false, [
18836
+ "action",
18837
+ "brandId",
18838
+ "confirmed"
18839
+ ], []),
18840
+ slot("brand_source", false, [
18841
+ "sourceUrl"
18842
+ ], []),
18843
+ slot("business_notes", false, [
18844
+ "businessNotes"
18845
+ ], []),
18846
+ slot("locale", false, [
18847
+ "language"
18848
+ ], [])
18849
+ ], [
18850
+ "owner_context"
18851
+ ], [
18852
+ "brand"
18853
+ ], MCP_ONLY_CHANNEL_EXPOSURE),
18854
+ defineCapability("create_service_landing", "f668f49e8006c610102ecb1760cdd9a238a9750c77aecf1762b58b8474e5495e", "credit_consuming", "creative", [
18855
+ "credit_consuming",
18856
+ "state_change"
18857
+ ], [
18858
+ slot("service_landing_action", false, [
18859
+ "action",
18860
+ "servicePageId",
18861
+ "operationId",
18862
+ "confirmed",
18863
+ "maxCredits"
18864
+ ], []),
18865
+ slot("brand", false, [
18866
+ "brandId"
18867
+ ], []),
18868
+ slot("business_info", false, [
18869
+ "businessInfo"
18870
+ ], []),
18871
+ slot("slug", false, [
18872
+ "slug"
18873
+ ], [])
18874
+ ], [
18875
+ "brand"
18876
+ ], [
18877
+ "service_landing"
18878
+ ], MCP_ONLY_CHANNEL_EXPOSURE),
18879
+ defineCapability("create_service_images", "1df35139abd6b04c5ebcdd5945154555a3f0d0e49b61f7d70c98cc5dad1e1347", "credit_consuming", "creative", [
18880
+ "credit_consuming",
18881
+ "state_change"
18882
+ ], [
18883
+ slot("brand", false, [
18884
+ "brandId"
18885
+ ], []),
18886
+ slot("service_brief", false, [
18887
+ "instruction",
18888
+ "delegateToAi"
18889
+ ], []),
18890
+ slot("format", false, [
18891
+ "format"
18892
+ ], []),
18893
+ slot("quantity", false, [
18894
+ "count"
18895
+ ], []),
18896
+ slot("quality", false, [
18897
+ "quality"
18898
+ ], []),
18899
+ slot("locale", false, [
18900
+ "language"
18901
+ ], []),
18902
+ slot("ad_copy", false, [
18903
+ "adCopy"
18904
+ ], []),
18905
+ slot("confirmation", false, [
18906
+ "confirmed"
18907
+ ], [])
18908
+ ], [
18909
+ "brand"
18910
+ ], [
18911
+ "task",
18912
+ "creative"
18913
+ ], MCP_ONLY_CHANNEL_EXPOSURE),
18780
18914
  // MCP-only recovery of an existing code edit. WhatsApp has no code-landing
18781
18915
  // editor or edit receipt input; it hands this recovery to ProductMaker Web.
18782
18916
  defineCapability("verify_landing_commerce", "4b98ea73f291428fa19ec91ec37deb44a776b76cb19b0d47aef20ee905da7d10", "external_mutation", "publishing", [
@@ -23463,6 +23597,8 @@ var PRODUCTMAKER_SERVER_INSTRUCTIONS = [
23463
23597
  "Preserve all price disclosures and explicit confirmations. New enabled classic landings include the initial product GIF and its cost by default; disclose that choice before confirming. Pass includeInitialLandingGif=false when the user excludes it, and preserve that flag on retries.",
23464
23598
  "For Shopify publication recovery, preserve the canonical recovery issues, revision and fingerprint. Ask the seller for missing values and confirm the exact currency, unit price and pack totals before submitting commerceCorrection with confirmed:true. Never invent prices, convert currency or erase a visible offer. Follow open_editor in ProductMaker when required; a changed context needs fresh review. commerceCorrectionSaved proves saved inputs only: check any uncertain publication in the task and store before another attempt, especially publicationMode=new.",
23465
23599
  "Before a paid creative call, show a compact summary of what will be generated and the commercial inputs, then wait for explicit confirmation. Whenever video is requested, ask videoAngleCount (1, 2 or 3) even if the user delegates creative decisions, because quantity changes cost. Use create_product_task for a combined/autopilot request and generate_video_creative when the user asks only for one to three videos. Every standalone image, landing, and video generation requires creativeSetup. Use guided unless the user explicitly delegates every creative decision; only then use automatic, which maps to the web AI-decides-all toggle. Ask each guided setup as one compact checklist, not a serial interrogation: image covers archetype, treatment, format, quantity, quality, style mix, ad copy, competitor scan, context, direction, colors, headline and subheadline; landing covers classic strategy, format, competitor scan, payment methods, context, direction, headline and subheadline; video covers one explicit yes/no market-video study choice, narrative preset, buyer persona, presenter and creative direction. The market study is OFF by default: omitting competitorScanEnabled means no study. Before asking that yes/no choice for a video or a combined task with video, call estimate_market_study_cost and show the user both totals (without and with the study); never set competitorScanEnabled=true unless the user accepted that study (or requested the narrativa an\xE1lisis de mercado). Callers can mix narratives per video using angleNarratives (e.g. ['market', 'review', 'sassy_product_pixar']) in create_product_task and generate_video_creative; 'market' represents the narrativa an\xE1lisis de mercado. Guided mode requests angle reviews, and video also requests script review; show every numbered angle candidate, let the user select up to the authorized videoAngleCount and apply edits only to the numbered angles they name. Warn before confirmation that multi-product combo mode currently skips the script-review pause. Show each returned draft and concrete choices, then wait for the user. When get_video_script returns ready or interactionState awaiting_user_review, present it once and NEVER call get_video_script or get_task_status again until the user answers. After resolving a video angle review, follow reviewNextAction and poll get_video_script directly until ready/error/approved/skipped; approved or skipped resumes get_task_status. After other resolve_pending_action calls or approve_video_script succeeds, resume get_task_status until another review or a terminal state.",
23600
+ "Route by business type before choosing tools. A service business or personal brand (barbershop, clinic, studio, academy, agency, consultant), its Instagram/Facebook/TikTok/website, or a request for a brand analysis uses the service journey: create_brand, then create_service_landing, then create_animated_video with the published landing URL and create_service_images with the brandId. A physical product, supplier, marketplace or AliExpress/Dropi link uses the dropshipping tools. Never mix them: never pass a product task to the service tools or a brandId to the product tools. If the type is unclear, ask one short question.",
23601
+ "In the service journey, run each step's free action first (create_brand action=start without confirmed, create_service_landing action=prepare), show its price, and wait for explicit acceptance before the paid call. Ask the user for missing business data (contact, area, services and prices) once, in a single message, and pass their own words as businessNotes or businessInfo; never invent them. Keep polling status while a step runs and follow each result's nextAction. Publish the landing only after the user agrees, then offer the animated video from its publicUrl and the service images.",
23466
23602
  "For an animated service video, call create_animated_video action=prepare first: URL capture is free and never generates. Poll action=status until ready or a terminal failure. Show the ready preview and exact format, duration, language and price, then wait for explicit user confirmation before action=generate with websiteCaptureId and confirmed=true. A blocked page needs another public URL without login; never ask for passwords. Do not treat capture readiness as permission to spend credits. Retry a lost generation receipt with the same websiteCaptureId and choices even after expiry: this replays the paid task. Keep generationRequestId unchanged when supplied. For another video explicitly requested and confirmed by the user, supply a fresh generationRequestId; never rotate it automatically after an error.",
23467
23603
  "ProductMaker Meta campaigns remain PAUSED.",
23468
23604
  "If the user asks for deeper Meta metrics and no suitable Meta tools are connected, suggest Meta's official MCP as an optional extension. Point only to Meta-owned documentation or repositories, never auto-install it or request tokens, and never claim that it is installed.",
@@ -23702,7 +23838,7 @@ var ApiClient = class {
23702
23838
  };
23703
23839
 
23704
23840
  // src/index.ts
23705
- var MCP_VERSION = "11.2.2";
23841
+ var MCP_VERSION = "11.3.0";
23706
23842
 
23707
23843
  // src/meta-upload-app.ts
23708
23844
  import { readFileSync } from "fs";
@@ -28002,8 +28138,756 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
28002
28138
  }
28003
28139
  __name(registerTaskTools, "registerTaskTools");
28004
28140
 
28005
- // src/tools/creatives.ts
28141
+ // src/tools/brands.ts
28006
28142
  import { z as z112 } from "zod";
28143
+ var guidance2 = getProductMakerCapabilityModelGuidance("create_brand");
28144
+ var BRAND_START_REPLAY_WINDOW_MS = 15 * 60 * 1e3;
28145
+ var BRAND_NOTES_MAX_CHARS = 2e4;
28146
+ var CreateBrandInput = z112.object({
28147
+ action: z112.enum([
28148
+ "start",
28149
+ "status",
28150
+ "list"
28151
+ ]).default("start").describe("start cotiza (sin confirmed) o inicia (confirmed=true) el an\xE1lisis; status consulta brandId hasta ready o failed; list muestra las marcas que el usuario ya tiene."),
28152
+ sourceUrl: z112.string().trim().min(1).max(500).optional().describe("Instagram, Facebook, TikTok o web p\xFAblica del negocio, como la dio el usuario (\xAB@sanlucasbarberia\xBB, \xABinstagram.com/sanlucasbarberia\xBB, \xABmibarberia.com\xBB). Obligatorio para start."),
28153
+ businessNotes: z112.string().trim().max(BRAND_NOTES_MAX_CHARS).optional().describe("Opcional. Lo que el usuario cuente de su negocio en el chat (servicios y precios, WhatsApp, ciudad o zona, horarios). Se guarda como documento del negocio y llega tal cual a la marca y a su landing. Copia sus palabras; no inventes datos."),
28154
+ language: z112.enum([
28155
+ "es",
28156
+ "en",
28157
+ "pt"
28158
+ ]).optional().describe("Idioma de la conversaci\xF3n. Solo se usa si la p\xE1gina del negocio no deja claro su idioma."),
28159
+ confirmed: z112.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio y que el usuario acepte. Sin confirmed, start solo cotiza y no cobra."),
28160
+ brandId: z112.string().uuid().optional().describe("ID de la marca. Obligatorio para status; no lo inventes.")
28161
+ }).strict();
28162
+ function normalizeBrandSourceUrl(raw) {
28163
+ const value = raw.trim();
28164
+ const handle = /^@([A-Za-z0-9._]{1,30})$/.exec(value);
28165
+ if (handle) return `https://www.instagram.com/${handle[1]}/`;
28166
+ return normalizeWebAddress(value);
28167
+ }
28168
+ __name(normalizeBrandSourceUrl, "normalizeBrandSourceUrl");
28169
+ function brandSourceKey(url) {
28170
+ try {
28171
+ const parsed = new URL(url);
28172
+ const host = parsed.hostname.toLowerCase().replace(/^www\./, "");
28173
+ const path3 = parsed.pathname.replace(/\/+$/, "").toLowerCase();
28174
+ return `${host}${path3}`;
28175
+ } catch {
28176
+ return url.trim().toLowerCase();
28177
+ }
28178
+ }
28179
+ __name(brandSourceKey, "brandSourceKey");
28180
+ var EXTRACTION_ERROR_TEXT = {
28181
+ invalid_url: "La direcci\xF3n no es v\xE1lida o no es p\xFAblica. Pide el enlace que abre sin iniciar sesi\xF3n.",
28182
+ not_business_page: "No encontramos contenido del negocio en esa p\xE1gina. Pide el Instagram o la web principal del negocio.",
28183
+ scrape_failed: "No pudimos abrir la p\xE1gina (puede estar ca\xEDda o bloqueando visitas autom\xE1ticas). Pide otro enlace del negocio.",
28184
+ scrape_too_large: "La p\xE1gina es demasiado grande. Pide otro enlace m\xE1s simple del negocio, por ejemplo su Instagram.",
28185
+ scrape_timeout: "La p\xE1gina tard\xF3 demasiado en responder. Se puede volver a intentar en unos minutos.",
28186
+ llm_failed: "Hubo un error temporal al analizar la marca. Se puede volver a intentar en unos minutos.",
28187
+ llm_invalid_output: "El an\xE1lisis devolvi\xF3 algo inesperado. Se puede volver a intentar.",
28188
+ worker_exception: "Ocurri\xF3 un error inesperado durante el an\xE1lisis. Se puede volver a intentar.",
28189
+ BRAND_EXTRACTION_REQUIRES_REVIEW: "El an\xE1lisis se detuvo de forma segura y necesita revisi\xF3n del equipo de ProductMaker antes de reintentarlo."
28190
+ };
28191
+ function describeExtractionError(code) {
28192
+ return (code ? EXTRACTION_ERROR_TEXT[code] : void 0) ?? "No pudimos analizar la marca. Se puede volver a intentar o probar con otro enlace del negocio.";
28193
+ }
28194
+ __name(describeExtractionError, "describeExtractionError");
28195
+ function paletteColors(palette) {
28196
+ if (!palette) return [];
28197
+ const colors = [];
28198
+ for (const key of [
28199
+ "primary",
28200
+ "secondary",
28201
+ "tertiary",
28202
+ "neutral"
28203
+ ]) {
28204
+ const value = palette[key];
28205
+ if (typeof value === "string") colors.push(value);
28206
+ }
28207
+ const accents = palette["accents"];
28208
+ if (Array.isArray(accents)) {
28209
+ for (const accent of accents) {
28210
+ if (typeof accent === "string") colors.push(accent);
28211
+ }
28212
+ }
28213
+ return colors.slice(0, 6);
28214
+ }
28215
+ __name(paletteColors, "paletteColors");
28216
+ function summarizeBrand(brand) {
28217
+ return {
28218
+ brandId: brand.id,
28219
+ status: brand.status,
28220
+ name: brand.name,
28221
+ ...brand.sourceUrl ? {
28222
+ source: brand.sourceUrl
28223
+ } : {},
28224
+ ...brand.oneLiner ? {
28225
+ oneLiner: brand.oneLiner
28226
+ } : {},
28227
+ ...brand.overview ? {
28228
+ overview: brand.overview
28229
+ } : {},
28230
+ tone: brand.toneAdjectives ?? [],
28231
+ values: brand.coreValues ?? [],
28232
+ keyMessages: brand.keyMessages ?? [],
28233
+ colors: paletteColors(brand.palette),
28234
+ hasLogo: Boolean(brand.logoKey),
28235
+ audiences: (brand.personas ?? []).map((persona) => ({
28236
+ name: persona.name,
28237
+ ...persona.demographics ? {
28238
+ who: persona.demographics
28239
+ } : {},
28240
+ ...persona.painPoints ? {
28241
+ painPoints: persona.painPoints
28242
+ } : {},
28243
+ ...persona.motivations ? {
28244
+ motivations: persona.motivations
28245
+ } : {}
28246
+ }))
28247
+ };
28248
+ }
28249
+ __name(summarizeBrand, "summarizeBrand");
28250
+ function describeBrandLanding(page) {
28251
+ if (!page) return null;
28252
+ return {
28253
+ servicePageId: page.id,
28254
+ status: page.publicUrl ? "published" : "draft",
28255
+ ...page.publicUrl ? {
28256
+ publicUrl: page.publicUrl
28257
+ } : {}
28258
+ };
28259
+ }
28260
+ __name(describeBrandLanding, "describeBrandLanding");
28261
+ function statusResponse(brand, landing = null) {
28262
+ if (brand.status === "ready") {
28263
+ const landingNext = landing?.publicUrl ? `Esta marca ya tiene su landing publicada en ${landing.publicUrl}: comp\xE1rtela y ofrece el video animado (create_animated_video con ese enlace) o im\xE1genes (create_service_images con este brandId).` : landing ? "Esta marca ya tiene una landing en borrador: ret\xF3mala con create_service_landing action=prepare y este brandId (no crea otra)." : "Luego ofrece crear su landing page con create_service_landing action=prepare y este brandId. Si pidi\xF3 tambi\xE9n un video, dile que va despu\xE9s de publicar la landing.";
28264
+ return asText({
28265
+ brand: summarizeBrand(brand),
28266
+ ...landing ? {
28267
+ serviceLanding: landing
28268
+ } : {},
28269
+ nextAction: `Muestra al usuario el an\xE1lisis de su marca (nombre, frase, tono, colores, mensajes clave y p\xFAblico) en pocas l\xEDneas. ${landingNext}`
28270
+ });
28271
+ }
28272
+ if (brand.status === "failed") {
28273
+ return asText({
28274
+ brandId: brand.id,
28275
+ status: "failed",
28276
+ reason: describeExtractionError(brand.extractionError),
28277
+ nextAction: "Explica el motivo en una frase y ofrece la salida que indica. No reintentes sin que el usuario lo pida: un nuevo an\xE1lisis se cobra de nuevo."
28278
+ });
28279
+ }
28280
+ return asText({
28281
+ brandId: brand.id,
28282
+ status: "extracting",
28283
+ nextAction: "El an\xE1lisis sigue en curso (suele tardar 1 a 3 minutos). Llama create_brand action=status con este brandId otra vez; no inicies otro an\xE1lisis."
28284
+ });
28285
+ }
28286
+ __name(statusResponse, "statusResponse");
28287
+ async function findRecentBrand(api, url, nowMs) {
28288
+ const brands = await api.getJson("/v1/brands");
28289
+ const key = brandSourceKey(url);
28290
+ return brands.find((brand) => {
28291
+ if (!brand.sourceUrl || brandSourceKey(brand.sourceUrl) !== key) return false;
28292
+ const updatedMs = Date.parse(brand.updatedAt);
28293
+ return brand.status !== "failed" && Number.isFinite(updatedMs) && nowMs - updatedMs < BRAND_START_REPLAY_WINDOW_MS;
28294
+ }) ?? null;
28295
+ }
28296
+ __name(findRecentBrand, "findRecentBrand");
28297
+ function registerBrandTools(server, api, options = {}) {
28298
+ const now = options.now ?? Date.now;
28299
+ async function findBrandLanding(brandId) {
28300
+ try {
28301
+ const { pages } = await api.getJson("/v1/service-pages");
28302
+ return describeBrandLanding(pages.find((page) => page.brandId === brandId) ?? null);
28303
+ } catch {
28304
+ return null;
28305
+ }
28306
+ }
28307
+ __name(findBrandLanding, "findBrandLanding");
28308
+ server.registerTool("create_brand", {
28309
+ description: guidance2.summary + " Sirve para NEGOCIOS DE SERVICIOS y marcas personales (barber\xEDa, consultorio, estudio, academia, agencia\u2026) y devuelve nombre, frase, tono, colores, logo, mensajes clave y p\xFAblico. Es el primer paso para su landing page (create_service_landing) y su video animado (create_animated_video). NO es para productos de dropshipping: para un producto, proveedor o enlace de AliExpress/Dropi usa las herramientas de producto. Flujo: action=start con sourceUrl (y businessNotes si el usuario cont\xF3 precios, WhatsApp o zona) devuelve el precio SIN cobrar; mu\xE9stralo y, cuando el usuario acepte, repite start con confirmed=true. Despu\xE9s llama action=status con el brandId hasta ready (1 a 3 minutos) o failed. Si el usuario ya tiene marcas, action=list te deja reutilizar una sin pagar otro an\xE1lisis. Repetir un start confirmado de la misma direcci\xF3n devuelve la marca que ya se est\xE1 analizando, sin cobrar dos veces.",
28310
+ inputSchema: CreateBrandInput.shape,
28311
+ annotations: CREATES_PAID_OPEN_WORLD
28312
+ }, async (rawInput) => {
28313
+ try {
28314
+ const input = CreateBrandInput.parse(rawInput);
28315
+ if (input.action === "list") {
28316
+ const brands = await api.getJson("/v1/brands");
28317
+ return asText({
28318
+ brands: brands.map((brand) => ({
28319
+ brandId: brand.id,
28320
+ name: brand.name,
28321
+ status: brand.status,
28322
+ ...brand.sourceUrl ? {
28323
+ source: brand.sourceUrl
28324
+ } : {}
28325
+ })),
28326
+ nextAction: brands.length === 0 ? "El usuario a\xFAn no tiene marcas. Pide el Instagram o la web de su negocio y usa action=start." : "Si una de estas es la marca del negocio, \xFAsala (create_brand action=status para ver su an\xE1lisis, o create_service_landing con su brandId). Si no, usa action=start."
28327
+ });
28328
+ }
28329
+ if (input.action === "status") {
28330
+ if (!input.brandId) {
28331
+ throw mcp(McpErrorCode.INVALID_INPUT, "brandId es obligatorio para consultar una marca. Usa el que devolvi\xF3 start o list.");
28332
+ }
28333
+ const brand = await api.getJson(`/v1/brands/${encodeURIComponent(input.brandId)}`);
28334
+ return statusResponse(brand, brand.status === "ready" ? await findBrandLanding(brand.id) : null);
28335
+ }
28336
+ const sourceUrl = input.sourceUrl ? normalizeBrandSourceUrl(input.sourceUrl) : null;
28337
+ if (!sourceUrl) {
28338
+ throw mcp(McpErrorCode.INVALID_INPUT, "sourceUrl debe ser el Instagram, Facebook, TikTok o la web p\xFAblica del negocio (ej. @mibarberia o mibarberia.com). No se cobr\xF3 nada.");
28339
+ }
28340
+ const existing = await findRecentBrand(api, sourceUrl, now());
28341
+ if (existing && input.confirmed === true) {
28342
+ const brand = await api.getJson(`/v1/brands/${encodeURIComponent(existing.id)}`);
28343
+ if (!input.businessNotes) return statusResponse(brand);
28344
+ return asText({
28345
+ brandId: brand.id,
28346
+ status: brand.status,
28347
+ replayed: true,
28348
+ notesSaved: false,
28349
+ nextAction: "Esta marca ya se estaba analizando (no se cobr\xF3 otra vez) y las notas del negocio NO entraron en este an\xE1lisis. P\xE1salas tal cual en businessInfo de create_service_landing (prepare o build) para que la landing las use. Sigue con create_brand action=status y este brandId."
28350
+ });
28351
+ }
28352
+ const estimate = await api.postJson("/v1/billing/estimate", {
28353
+ type: "brand-extract",
28354
+ options: {}
28355
+ });
28356
+ const priceCredits = Math.ceil(estimate.estimatedMaxCostCredits);
28357
+ if (input.confirmed !== true) {
28358
+ return asText({
28359
+ sourceUrl,
28360
+ charged: false,
28361
+ ...existing ? {
28362
+ existingBrand: {
28363
+ brandId: existing.id,
28364
+ status: existing.status
28365
+ }
28366
+ } : {},
28367
+ priceCredits,
28368
+ userBalanceCredits: estimate.userBalanceCredits,
28369
+ hasSufficientBalance: estimate.hasSufficientBalance,
28370
+ nextAction: existing ? `Esta direcci\xF3n ya tiene una marca analizada hace pocos minutos (brandId ${existing.id}): \xFAsala con create_brand action=status, sin pagar otro an\xE1lisis. Si el usuario cont\xF3 datos nuevos del negocio, p\xE1salos en businessInfo de create_service_landing.` : estimate.hasSufficientBalance ? `Dile al usuario que el an\xE1lisis de su marca cuesta hasta ${priceCredits} cr\xE9ditos y preg\xFAntale si quiere hacerlo. Solo si acepta, llama create_brand action=start con el mismo sourceUrl${input.businessNotes ? ", las mismas businessNotes" : ""} y confirmed=true.` : `El saldo (${estimate.userBalanceCredits} cr\xE9ditos) no alcanza para el an\xE1lisis (${priceCredits} cr\xE9ditos). Dile que recargue en productmaker.app y no inicies nada.`
28371
+ });
28372
+ }
28373
+ await requirePaidCreativeConfirmation(server, `An\xE1lisis de marca de ${sourceUrl}${input.businessNotes ? " con los datos que contaste del negocio" : ""}. Precio: hasta ${priceCredits} cr\xE9ditos.`);
28374
+ const notes = input.businessNotes?.trim();
28375
+ const created = await api.postMultipart("/v1/brands/extract", notes ? {
28376
+ files: {
28377
+ buffer: Buffer.from(notes, "utf8"),
28378
+ mimetype: "text/plain",
28379
+ filename: "notas-del-negocio.txt"
28380
+ }
28381
+ } : {}, {
28382
+ url: sourceUrl,
28383
+ locale: input.language
28384
+ });
28385
+ return asText({
28386
+ brandId: created.brandId,
28387
+ status: "extracting",
28388
+ priceCredits,
28389
+ nextAction: "El an\xE1lisis empez\xF3 (1 a 3 minutos). Dile al usuario que lo est\xE1s preparando y llama create_brand action=status con este brandId hasta ready o failed."
28390
+ });
28391
+ } catch (error) {
28392
+ throw translateError(error);
28393
+ }
28394
+ });
28395
+ }
28396
+ __name(registerBrandTools, "registerBrandTools");
28397
+
28398
+ // src/tools/service-landing.ts
28399
+ import { createHash as createHash4 } from "crypto";
28400
+ import { z as z113 } from "zod";
28401
+ var guidance3 = getProductMakerCapabilityModelGuidance("create_service_landing");
28402
+ var SERVICE_LANDING_KEY_NAMESPACE = "6f1d8c2a-3b4e-5f60-8a7b-9c0d1e2f3a4b";
28403
+ var SERVICE_LANDING_INFO_MAX_CHARS = 2e4;
28404
+ var CreateServiceLandingInput = z113.object({
28405
+ action: z113.enum([
28406
+ "prepare",
28407
+ "build",
28408
+ "status",
28409
+ "publish"
28410
+ ]).default("prepare").describe("prepare (gratis) crea o retoma la landing de la marca y da el precio; build (pagado, confirmed=true) lee los datos del negocio y luego crea la p\xE1gina; status sigue el avance; publish la publica y devuelve su enlace."),
28411
+ brandId: z113.string().uuid().optional().describe("ID de la marca (de create_brand). Obligatorio para prepare. Nunca un taskId de producto."),
28412
+ servicePageId: z113.string().uuid().optional().describe("ID de la landing que devolvi\xF3 prepare. Obligatorio para build, status y publish."),
28413
+ businessInfo: z113.string().trim().max(SERVICE_LANDING_INFO_MAX_CHARS).optional().describe("Opcional. Lo que el usuario cont\xF3 del negocio y no estaba en su marca: servicios y precios, WhatsApp o tel\xE9fono, ciudad o zona, horarios. Copia sus palabras; no inventes datos."),
28414
+ confirmed: z113.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio total y que el usuario acepte. Obligatorio para build."),
28415
+ maxCredits: z113.number().int().positive().optional().describe("El total en cr\xE9ditos que el usuario acept\xF3 (el que devolvi\xF3 prepare). Obligatorio para build: ninguna etapa se inicia si su precio supera este total."),
28416
+ operationId: z113.string().uuid().optional().describe("Opcional en status: la operaci\xF3n que devolvi\xF3 build."),
28417
+ slug: z113.string().trim().toLowerCase().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).max(64).optional().describe("Opcional en publish: la direcci\xF3n corta de la p\xE1gina (ej. sanlucas-barberia). Si falta se usa la sugerida desde el nombre del negocio.")
28418
+ }).strict();
28419
+ function serviceKey(...parts) {
28420
+ return deriveUuidV5(parts.join(":"), SERVICE_LANDING_KEY_NAMESPACE);
28421
+ }
28422
+ __name(serviceKey, "serviceKey");
28423
+ function textHash(text2) {
28424
+ return createHash4("sha256").update(text2, "utf8").digest("hex").slice(0, 16);
28425
+ }
28426
+ __name(textHash, "textHash");
28427
+ function quoteRange(quote) {
28428
+ const max = Math.ceil(quote.estimatedMaxCostCredits ?? quote.credits);
28429
+ const min = Math.ceil(Math.min(quote.estimatedMinCostCredits ?? max, max));
28430
+ return {
28431
+ min,
28432
+ max
28433
+ };
28434
+ }
28435
+ __name(quoteRange, "quoteRange");
28436
+ function serviceApiCode(error) {
28437
+ if (!(error instanceof ApiHttpError)) return null;
28438
+ const body = error.body;
28439
+ if (!body || typeof body !== "object") return null;
28440
+ const record = body;
28441
+ for (const value of [
28442
+ record.code,
28443
+ record.errorCode,
28444
+ record.message
28445
+ ]) {
28446
+ if (typeof value === "string" && /^[A-Z][A-Z0-9_]{2,63}$/.test(value)) return value;
28447
+ }
28448
+ return null;
28449
+ }
28450
+ __name(serviceApiCode, "serviceApiCode");
28451
+ function suggestServiceSlug(name) {
28452
+ const slug = (name ?? "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48).replace(/-+$/g, "");
28453
+ return slug || "mi-servicio";
28454
+ }
28455
+ __name(suggestServiceSlug, "suggestServiceSlug");
28456
+ function serviceEditorUrl(api, pageId) {
28457
+ try {
28458
+ const url = new URL(api.publicUrl("/"));
28459
+ if (!url.hostname.startsWith("api.")) return null;
28460
+ url.hostname = url.hostname.slice("api.".length);
28461
+ url.pathname = `/es/service-pages/${encodeURIComponent(pageId)}/edit`;
28462
+ return url.toString();
28463
+ } catch {
28464
+ return null;
28465
+ }
28466
+ }
28467
+ __name(serviceEditorUrl, "serviceEditorUrl");
28468
+ var MISSING_LABELS = {
28469
+ "business.name": "nombre del negocio",
28470
+ services: "servicios (y su precio, si lo tiene)",
28471
+ "contact.channels": "WhatsApp, tel\xE9fono o enlace para reservar",
28472
+ "contact.primaryCta": "c\xF3mo quiere que lo contacten primero"
28473
+ };
28474
+ function describeMissing(fields) {
28475
+ return [
28476
+ ...new Set(fields.map((field) => MISSING_LABELS[field]).filter((label) => Boolean(label)))
28477
+ ];
28478
+ }
28479
+ __name(describeMissing, "describeMissing");
28480
+ function summarizeFacts(facts) {
28481
+ return {
28482
+ ...facts.business.name ? {
28483
+ business: facts.business.name
28484
+ } : {},
28485
+ ...facts.business.description ? {
28486
+ description: facts.business.description
28487
+ } : {},
28488
+ ...facts.business.coverage ? {
28489
+ area: facts.business.coverage
28490
+ } : {},
28491
+ services: facts.services.filter((service) => service.name).map((service) => ({
28492
+ name: service.name,
28493
+ ...service.price ? {
28494
+ price: service.price
28495
+ } : {}
28496
+ })),
28497
+ contact: facts.contact.channels.map((channel) => channel.value ? `${channel.kind}: ${channel.value}` : channel.kind)
28498
+ };
28499
+ }
28500
+ __name(summarizeFacts, "summarizeFacts");
28501
+ var OPERATION_ERROR_TEXT = {
28502
+ SERVICE_PAGE_SOURCES_REQUIRED: "No hay informaci\xF3n del negocio para escribir la p\xE1gina. Pide al usuario que cuente sus servicios y vuelve a llamar build con businessInfo.",
28503
+ SOURCE_TEXT_REQUIRED: "No hay informaci\xF3n del negocio para escribir la p\xE1gina. Pide al usuario que cuente sus servicios y vuelve a llamar build con businessInfo.",
28504
+ SERVICE_PAGE_PROVIDER_REJECTED: "El servicio de IA no estuvo disponible y no se cobr\xF3. Ofrece reintentar en unos minutos con build (mismo maxCredits)."
28505
+ };
28506
+ function operationFailureText(code) {
28507
+ return (code ? OPERATION_ERROR_TEXT[code] : void 0) ?? "La etapa fall\xF3 y no se cobr\xF3. Ofrece volver a intentarlo con build (mismo maxCredits); si vuelve a fallar, sugiere terminarla en la web de ProductMaker.";
28508
+ }
28509
+ __name(operationFailureText, "operationFailureText");
28510
+ function isPageGenerated(page) {
28511
+ return page.artifact !== null && page.artifact !== void 0;
28512
+ }
28513
+ __name(isPageGenerated, "isPageGenerated");
28514
+ function registerServiceLandingTools(server, api) {
28515
+ const pagePath = /* @__PURE__ */ __name((id) => `/v1/service-pages/${encodeURIComponent(id)}`, "pagePath");
28516
+ const readPage = /* @__PURE__ */ __name((id) => api.getJson(pagePath(id)), "readPage");
28517
+ async function findPageForBrand(brandId) {
28518
+ const { pages } = await api.getJson("/v1/service-pages");
28519
+ return pages.find((page) => page.brandId === brandId) ?? null;
28520
+ }
28521
+ __name(findPageForBrand, "findPageForBrand");
28522
+ async function addBusinessInfo(page, info) {
28523
+ try {
28524
+ await api.postMultipart(`${pagePath(page.id)}/intake`, {}, {
28525
+ expectedRevision: String(page.draftRevision),
28526
+ idempotencyKey: serviceKey(page.id, "intake", textHash(info)),
28527
+ text: info
28528
+ });
28529
+ } catch (error) {
28530
+ if (serviceApiCode(error) !== "SERVICE_PAGE_CONFLICT_REVISION") throw error;
28531
+ }
28532
+ return readPage(page.id);
28533
+ }
28534
+ __name(addBusinessInfo, "addBusinessInfo");
28535
+ async function quote(page, purpose) {
28536
+ return api.postJson(`${pagePath(page.id)}/quotes/${purpose}`, {
28537
+ expectedRevision: page.draftRevision
28538
+ });
28539
+ }
28540
+ __name(quote, "quote");
28541
+ async function startOperation(page, purpose, quoted) {
28542
+ const path3 = purpose === "fact-extraction" ? `${pagePath(page.id)}/intake/extract` : `${pagePath(page.id)}/generate`;
28543
+ return api.postJson(path3, {
28544
+ quoteId: quoted.quoteId,
28545
+ idempotencyKey: serviceKey(page.id, purpose, page.draftRevision),
28546
+ expectedRevision: page.draftRevision
28547
+ });
28548
+ }
28549
+ __name(startOperation, "startOperation");
28550
+ function generatedResponse(page) {
28551
+ const editorUrl = serviceEditorUrl(api, page.id);
28552
+ return asText({
28553
+ servicePageId: page.id,
28554
+ status: "generated",
28555
+ ...page.publicUrl ? {
28556
+ publicUrl: page.publicUrl
28557
+ } : {},
28558
+ ...editorUrl ? {
28559
+ editorUrl
28560
+ } : {},
28561
+ suggestedSlug: page.slug ?? suggestServiceSlug(page.businessName),
28562
+ nextAction: page.publicUrl ? `La p\xE1gina ya est\xE1 publicada en ${page.publicUrl}. Si el usuario pidi\xF3 un video, sigue con create_animated_video action=prepare y landingUrl=${page.publicUrl}; si pidi\xF3 im\xE1genes, con create_service_images y brandId=${page.brandId ?? "(el de su marca)"}.` : `La p\xE1gina est\xE1 lista. ${editorUrl ? `Dale al usuario el enlace para verla y editarla (${editorUrl}). ` : ""}Preg\xFAntale si la publicas con la direcci\xF3n sugerida (o la que prefiera) y, si acepta, llama action=publish.`
28563
+ });
28564
+ }
28565
+ __name(generatedResponse, "generatedResponse");
28566
+ server.registerTool("create_service_landing", {
28567
+ description: guidance3.summary + " Parte del brandId de create_brand y termina con el enlace p\xFAblico de la p\xE1gina. NO es para productos de dropshipping (para eso generate_landing / create_product_task). Flujo: 1) action=prepare con brandId (gratis): crea o retoma la landing y devuelve el precio estimado total. 2) Antes de cobrar, pregunta UNA sola vez lo que el usuario no haya dicho ya (WhatsApp o tel\xE9fono, ciudad o zona, servicios y precios); puede omitirlo. 3) Cuando acepte el precio, action=build con servicePageId, confirmed=true, maxCredits y businessInfo con sus respuestas: primero lee los datos del negocio. 4) action=status con servicePageId y operationId hasta que termine; cuando lo indique, vuelve a llamar build con el mismo maxCredits para crear la p\xE1gina (1 a 3 minutos). 5) Muestra el enlace para verla y, si acepta, action=publish. Repetir una llamada no cobra dos veces. Con el enlace publicado sigue create_animated_video si el usuario quiere un video, y create_service_images (con el mismo brandId) si quiere im\xE1genes.",
28568
+ inputSchema: CreateServiceLandingInput.shape,
28569
+ annotations: CREATES_PAID
28570
+ }, async (rawInput) => {
28571
+ try {
28572
+ const input = CreateServiceLandingInput.parse(rawInput);
28573
+ if (input.action === "prepare") {
28574
+ if (!input.brandId) {
28575
+ throw mcp(McpErrorCode.INVALID_INPUT, "brandId es obligatorio: primero analiza la marca con create_brand (o elige una con create_brand action=list). No se cobr\xF3 nada.");
28576
+ }
28577
+ const brand = await api.getJson(`/v1/brands/${encodeURIComponent(input.brandId)}`);
28578
+ if (brand.status !== "ready") {
28579
+ return asText({
28580
+ brandId: input.brandId,
28581
+ charged: false,
28582
+ nextAction: brand.status === "extracting" ? "La marca todav\xEDa se est\xE1 analizando. Llama create_brand action=status hasta ready y luego vuelve a prepare." : "El an\xE1lisis de esta marca fall\xF3. Revisa create_brand action=status y ofrece analizarla de nuevo."
28583
+ });
28584
+ }
28585
+ const existing = await findPageForBrand(input.brandId);
28586
+ let page2 = existing ? await readPage(existing.id) : await api.postJson("/v1/service-pages", {
28587
+ brandId: input.brandId,
28588
+ includeBrandLogo: Boolean(brand.logoKey),
28589
+ idempotencyKey: serviceKey(input.brandId, "create")
28590
+ });
28591
+ if (isPageGenerated(page2)) return generatedResponse(page2);
28592
+ if (input.businessInfo) page2 = await addBusinessInfo(page2, input.businessInfo);
28593
+ const factsQuote = page2.factExtractionCompleted ? null : await quote(page2, "fact-extraction");
28594
+ const generationQuote = await quote(page2, "generation");
28595
+ const facts = factsQuote ? quoteRange(factsQuote) : {
28596
+ min: 0,
28597
+ max: 0
28598
+ };
28599
+ const build = quoteRange(generationQuote);
28600
+ const totalMin = facts.min + build.min;
28601
+ const totalCredits = facts.max + build.max;
28602
+ const requiredBalanceCredits = Math.max(factsQuote?.credits ?? 0, generationQuote.credits);
28603
+ const priceText = totalMin === totalCredits ? `${totalCredits} cr\xE9ditos` : `entre ${totalMin} y ${totalCredits} cr\xE9ditos`;
28604
+ return asText({
28605
+ servicePageId: page2.id,
28606
+ charged: false,
28607
+ ...existing ? {
28608
+ resumed: true
28609
+ } : {},
28610
+ business: page2.businessName,
28611
+ known: summarizeFacts(page2.facts),
28612
+ price: {
28613
+ ...factsQuote ? {
28614
+ readBusinessInfoCredits: facts
28615
+ } : {},
28616
+ buildPageCredits: build,
28617
+ totalMinCredits: totalMin,
28618
+ totalCredits,
28619
+ requiredBalanceCredits
28620
+ },
28621
+ nextAction: `En un solo mensaje: dile al usuario que crear su landing cuesta ${priceText} en total (se cobra lo que se use; necesita al menos ${requiredBalanceCredits} cr\xE9ditos de saldo para empezar) y preg\xFAntale, solo si no lo dijo ya en esta conversaci\xF3n, su WhatsApp o tel\xE9fono, su ciudad o zona y sus servicios con precio (puede omitirlo). Cuando acepte el precio, llama action=build con servicePageId=${page2.id}, confirmed=true, maxCredits=${totalCredits} y businessInfo con lo que respondi\xF3.`
28622
+ });
28623
+ }
28624
+ if (!input.servicePageId) {
28625
+ throw mcp(McpErrorCode.INVALID_INPUT, "servicePageId es obligatorio: usa el que devolvi\xF3 prepare. No se cobr\xF3 nada.");
28626
+ }
28627
+ if (input.action === "status") {
28628
+ let operation2 = null;
28629
+ if (input.operationId) {
28630
+ operation2 = await api.getJson(`/v1/service-pages/operations/${encodeURIComponent(input.operationId)}`);
28631
+ if (operation2.recoverable && (operation2.status === "failed" || operation2.status === "requires_reconciliation")) {
28632
+ operation2 = await api.postJson(`${pagePath(input.servicePageId)}/operations/${encodeURIComponent(operation2.opId)}/recover`, {});
28633
+ }
28634
+ if (operation2.status === "queued" || operation2.status === "running") {
28635
+ return asText({
28636
+ servicePageId: input.servicePageId,
28637
+ operationId: operation2.opId,
28638
+ step: operation2.kind === "generation" ? "creando la p\xE1gina" : "leyendo los datos del negocio",
28639
+ status: operation2.status,
28640
+ nextAction: "Sigue en curso. Llama action=status otra vez con el mismo operationId en unos segundos; no inicies otra etapa."
28641
+ });
28642
+ }
28643
+ if (operation2.status === "requires_reconciliation") {
28644
+ return asText({
28645
+ servicePageId: input.servicePageId,
28646
+ operationId: operation2.opId,
28647
+ status: "in_review",
28648
+ // Unknown on purpose: the charge is exactly what is being
28649
+ // confirmed. Settlement only charges a completed step.
28650
+ nextAction: "Esta etapa qued\xF3 en revisi\xF3n por un problema temporal: estamos confirmando si se complet\xF3. Si no se complet\xF3, no se cobra. Dile al usuario que sus datos est\xE1n guardados; vuelve a llamar action=status con el mismo operationId en unos minutos. No inicies otra etapa mientras tanto; si sigue igual, sugiere escribir a soporte de ProductMaker desde la web."
28651
+ });
28652
+ }
28653
+ if (operation2.status !== "succeeded") {
28654
+ return asText({
28655
+ servicePageId: input.servicePageId,
28656
+ operationId: operation2.opId,
28657
+ status: "failed",
28658
+ charged: false,
28659
+ reason: operationFailureText(operation2.errorCode)
28660
+ });
28661
+ }
28662
+ }
28663
+ const page2 = await readPage(input.servicePageId);
28664
+ if (isPageGenerated(page2)) return generatedResponse(page2);
28665
+ const missing = describeMissing(page2.missingFields);
28666
+ return asText({
28667
+ servicePageId: page2.id,
28668
+ status: page2.factExtractionCompleted ? "business_info_read" : "waiting_for_build",
28669
+ found: summarizeFacts(page2.facts),
28670
+ ...missing.length ? {
28671
+ stillMissing: missing
28672
+ } : {},
28673
+ nextAction: page2.factExtractionCompleted ? `Muestra en pocas l\xEDneas lo que encontramos del negocio${missing.length ? " y lo que falta (puede agregarlo en businessInfo o seguir sin eso)" : ""}. Luego llama action=build con el mismo servicePageId, confirmed=true y el mismo maxCredits para crear la p\xE1gina.` : "A\xFAn no se leyeron los datos del negocio. Llama action=build con confirmed=true y el maxCredits que acept\xF3 el usuario."
28674
+ });
28675
+ }
28676
+ if (input.action === "publish") {
28677
+ const page2 = await readPage(input.servicePageId);
28678
+ if (!isPageGenerated(page2)) {
28679
+ throw mcp(McpErrorCode.INVALID_INPUT, "La p\xE1gina todav\xEDa no est\xE1 creada. Termina action=build y action=status antes de publicar.");
28680
+ }
28681
+ if (page2.publicUrl) return generatedResponse(page2);
28682
+ const slug = input.slug ?? suggestServiceSlug(page2.businessName);
28683
+ try {
28684
+ const published = await api.postJson(`${pagePath(page2.id)}/publish`, {
28685
+ expectedRevision: page2.draftRevision,
28686
+ slug,
28687
+ idempotencyKey: serviceKey(page2.id, "publish", slug)
28688
+ });
28689
+ return asText({
28690
+ servicePageId: page2.id,
28691
+ status: "published",
28692
+ publicUrl: published.publicUrl,
28693
+ nextAction: `Comparte el enlace ${published.publicUrl} con el usuario. Si pidi\xF3 un video, sigue con create_animated_video action=prepare y landingUrl=${published.publicUrl}; si pidi\xF3 im\xE1genes, con create_service_images y brandId=${page2.brandId ?? "(el de su marca)"}.`
28694
+ });
28695
+ } catch (error) {
28696
+ if (serviceApiCode(error)?.includes("SLUG")) {
28697
+ return asText({
28698
+ servicePageId: page2.id,
28699
+ status: "slug_taken",
28700
+ slug,
28701
+ nextAction: `La direcci\xF3n \xAB${slug}\xBB ya est\xE1 en uso. Prop\xF3n otra (por ejemplo \xAB${slug}-${(/* @__PURE__ */ new Date()).getFullYear()}\xBB o con la ciudad) y llama action=publish con slug.`
28702
+ });
28703
+ }
28704
+ throw error;
28705
+ }
28706
+ }
28707
+ if (input.confirmed !== true || !input.maxCredits) {
28708
+ throw mcp(McpErrorCode.INVALID_INPUT, "build cobra cr\xE9ditos: primero llama action=prepare, muestra el precio total y, cuando el usuario acepte, env\xEDa confirmed=true y maxCredits. No se cobr\xF3 nada.");
28709
+ }
28710
+ let page = await readPage(input.servicePageId);
28711
+ if (isPageGenerated(page)) return generatedResponse(page);
28712
+ const revisionBefore = page.draftRevision;
28713
+ if (input.businessInfo) page = await addBusinessInfo(page, input.businessInfo);
28714
+ const addedInfo = page.draftRevision !== revisionBefore;
28715
+ const needsFacts = !page.factExtractionCompleted || addedInfo;
28716
+ const purpose = needsFacts ? "fact-extraction" : "generation";
28717
+ const quoted = await quote(page, purpose);
28718
+ const credits = quoteRange(quoted).max;
28719
+ if (credits > input.maxCredits) {
28720
+ return asText({
28721
+ servicePageId: page.id,
28722
+ charged: false,
28723
+ priceCredits: credits,
28724
+ nextAction: `El precio subi\xF3 a ${credits} cr\xE9ditos, m\xE1s de lo aceptado (${input.maxCredits}). Preg\xFAntale al usuario si sigue y, si acepta, repite build con maxCredits=${credits}.`
28725
+ });
28726
+ }
28727
+ await requirePaidCreativeConfirmation(server, needsFacts ? `Leer los datos del negocio para su landing. Precio: hasta ${credits} cr\xE9ditos (total aceptado: ${input.maxCredits}).` : `Crear la landing page del negocio. Precio: hasta ${credits} cr\xE9ditos (total aceptado: ${input.maxCredits}).`);
28728
+ const operation = await startOperation(page, purpose, quoted);
28729
+ return asText({
28730
+ servicePageId: page.id,
28731
+ operationId: operation.opId,
28732
+ step: needsFacts ? "leyendo los datos del negocio" : "creando la p\xE1gina",
28733
+ status: operation.status,
28734
+ priceCredits: credits,
28735
+ nextAction: needsFacts ? "Dile al usuario que est\xE1s leyendo los datos de su negocio. Llama action=status con servicePageId y este operationId hasta que termine." : "Dile al usuario que est\xE1s creando su p\xE1gina (1 a 3 minutos). Llama action=status con servicePageId y este operationId hasta que termine."
28736
+ });
28737
+ } catch (error) {
28738
+ throw translateError(error);
28739
+ }
28740
+ });
28741
+ }
28742
+ __name(registerServiceLandingTools, "registerServiceLandingTools");
28743
+
28744
+ // src/tools/service-images.ts
28745
+ import { z as z114 } from "zod";
28746
+ var guidance4 = getProductMakerCapabilityModelGuidance("create_service_images");
28747
+ var SERVICE_IMAGE_FORMATS = [
28748
+ "4:5",
28749
+ "9:16",
28750
+ "1:1"
28751
+ ];
28752
+ var SERVICE_IMAGE_MAX_COUNT = 3;
28753
+ var LANGUAGE_TAGS = {
28754
+ es: "es-CO",
28755
+ en: "en-US",
28756
+ pt: "pt-BR"
28757
+ };
28758
+ var CreateServiceImagesInput = z114.object({
28759
+ brandId: z114.string().uuid().optional().describe("ID de la marca (de create_brand). Recomendado: da nombre, colores, tono y servicios. Nunca un taskId de producto."),
28760
+ instruction: z114.string().trim().max(4e3).optional().describe("Qu\xE9 servicio o promoci\xF3n mostrar, con las palabras del usuario (ej. \xABcorte + barba a $35.000 los martes\xBB). Si el usuario deja que la IA decida, om\xEDtelo y usa delegateToAi=true."),
28761
+ delegateToAi: z114.boolean().optional().describe("true si el usuario prefiere que la IA elija qu\xE9 mostrar a partir de su marca."),
28762
+ format: z114.enum(SERVICE_IMAGE_FORMATS).optional().describe('Formato: "4:5" (feed, por defecto), "9:16" (historias/reels) o "1:1" (cuadrado).'),
28763
+ count: z114.number().int().min(1).max(SERVICE_IMAGE_MAX_COUNT).optional().describe("Cu\xE1ntas im\xE1genes, de 1 a 3 (por defecto 1)."),
28764
+ quality: z114.enum([
28765
+ "standard",
28766
+ "high"
28767
+ ]).optional().describe('Calidad: "standard" (por defecto) o "high" (m\xE1s cara; no disponible en 4:5).'),
28768
+ language: z114.enum([
28769
+ "es",
28770
+ "en",
28771
+ "pt"
28772
+ ]).optional().describe("Idioma de los textos de la imagen (por defecto es)."),
28773
+ adCopy: z114.boolean().optional().describe("Opcional. true agrega textos para el anuncio (titular y descripci\xF3n). Por defecto false."),
28774
+ confirmed: z114.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio y que el usuario acepte. Sin confirmed, solo cotiza y no cobra.")
28775
+ }).strict();
28776
+ function serviceImageModel(quality) {
28777
+ return quality === "high" ? AD_CREATIVE_MODELS.PREMIUM : AD_CREATIVE_MODELS.STANDARD;
28778
+ }
28779
+ __name(serviceImageModel, "serviceImageModel");
28780
+ function registerServiceImageTools(server, api, options = {}) {
28781
+ const now = options.now ?? Date.now;
28782
+ server.registerTool("create_service_images", {
28783
+ description: guidance4.summary + " Im\xE1genes para anuncios de un NEGOCIO DE SERVICIOS o marca personal, con su marca (brandId de create_brand), sin foto de producto. NO es para productos de dropshipping (para eso generate_image_creatives). Llama primero sin confirmed: devuelve el precio SIN cobrar; mu\xE9stralo y, cuando el usuario acepte, repite la llamada igual con confirmed=true. Pregunta qu\xE9 servicio o promoci\xF3n mostrar si el usuario no lo dijo (o usa delegateToAi=true si prefiere que la IA elija). Devuelve un taskId: sigue get_task_status hasta done (trae las im\xE1genes) o error. Repetir la misma llamada confirmada no cobra dos veces.",
28784
+ inputSchema: CreateServiceImagesInput.shape,
28785
+ annotations: CREATES_PAID
28786
+ }, async (rawInput) => {
28787
+ try {
28788
+ const input = CreateServiceImagesInput.parse(rawInput);
28789
+ const brief = ServiceImageInputSchema.safeParse({
28790
+ instruction: input.instruction ?? "",
28791
+ intent: input.delegateToAi ? "delegate_to_ai" : "user_brief"
28792
+ });
28793
+ if (!brief.success) {
28794
+ throw mcp(McpErrorCode.INVALID_INPUT, "Pregunta al usuario qu\xE9 servicio o promoci\xF3n quiere mostrar (instruction), o usa delegateToAi=true si prefiere que la IA elija. No se cobr\xF3 nada.");
28795
+ }
28796
+ const format = input.format ?? "4:5";
28797
+ const count = input.count ?? 1;
28798
+ const imageModel = serviceImageModel(input.quality ?? "standard");
28799
+ const tierError = validateImageTierForRatio(format, imageModel);
28800
+ if (tierError) {
28801
+ throw mcp(McpErrorCode.INVALID_INPUT, `${tierError} Usa quality="standard" o otro formato. No se cobr\xF3 nada.`);
28802
+ }
28803
+ const language = LANGUAGE_TAGS[input.language ?? "es"];
28804
+ const adCopy = input.adCopy ?? false;
28805
+ const estimate = await api.postJson("/v1/billing/estimate", {
28806
+ type: "image-creative",
28807
+ options: {
28808
+ subject: "service",
28809
+ nVariants: count,
28810
+ imageModel,
28811
+ aspectRatio: format,
28812
+ generateAdCopy: adCopy,
28813
+ requiresExtraction: false,
28814
+ requiresAngles: false,
28815
+ competitorScanEnabled: false
28816
+ }
28817
+ });
28818
+ const max = Math.ceil(estimate.estimatedMaxCostCredits);
28819
+ const min = Math.ceil(Math.min(estimate.estimatedMinCostCredits ?? max, max));
28820
+ const priceText = min === max ? `${max} cr\xE9ditos` : `entre ${min} y ${max} cr\xE9ditos`;
28821
+ const summary = `${count} imagen${count === 1 ? "" : "es"} ${format} de calidad ${input.quality === "high" ? "alta" : "est\xE1ndar"}${input.brandId ? " con la marca" : ""}`;
28822
+ if (input.confirmed !== true) {
28823
+ return asText({
28824
+ charged: false,
28825
+ images: {
28826
+ count,
28827
+ format,
28828
+ quality: input.quality ?? "standard"
28829
+ },
28830
+ priceCredits: {
28831
+ min,
28832
+ max
28833
+ },
28834
+ userBalanceCredits: estimate.userBalanceCredits,
28835
+ nextAction: estimate.hasSufficientBalance ? `Dile al usuario que ${summary} cuesta ${priceText} y preg\xFAntale si sigue. Solo si acepta, repite la llamada con los mismos datos y confirmed=true.` : `El saldo (${estimate.userBalanceCredits} cr\xE9ditos) no alcanza (${priceText}). Dile que recargue en productmaker.app y no generes nada.`
28836
+ });
28837
+ }
28838
+ await requirePaidCreativeConfirmation(server, `${summary}. Precio: ${priceText}.`);
28839
+ const fields = {
28840
+ serviceBrief: JSON.stringify(brief.data),
28841
+ brandId: input.brandId,
28842
+ aspectRatio: format,
28843
+ imageModel,
28844
+ nVariants: String(count),
28845
+ language,
28846
+ competitorScanEnabled: "false",
28847
+ generateAdCopy: String(adCopy)
28848
+ };
28849
+ const created = await api.postMultipart("/v1/creatives/images", {}, fields, {
28850
+ idempotencyKey: derivePaidRequestKey({
28851
+ fingerprint: api.fingerprint(),
28852
+ tool: "create_service_images",
28853
+ input: {
28854
+ brief: brief.data,
28855
+ brandId: input.brandId ?? null,
28856
+ format,
28857
+ count,
28858
+ imageModel,
28859
+ language,
28860
+ adCopy
28861
+ },
28862
+ nowMs: now()
28863
+ })
28864
+ });
28865
+ const taskId = created.taskId ?? created.jobId;
28866
+ if (!taskId) {
28867
+ throw new Error("ProductMaker no devolvi\xF3 el id de la tarea de im\xE1genes.");
28868
+ }
28869
+ return asText({
28870
+ taskId,
28871
+ status: created.status ?? "queued",
28872
+ ...created.replayed === true ? {
28873
+ replayed: true
28874
+ } : {},
28875
+ priceCredits: {
28876
+ min,
28877
+ max
28878
+ },
28879
+ nextAction: "Dile al usuario que est\xE1s creando sus im\xE1genes. Llama get_task_status con este taskId (waitSeconds=45) hasta done (trae las im\xE1genes) o error."
28880
+ });
28881
+ } catch (error) {
28882
+ if (error instanceof z114.ZodError) throw invalidInputFromZod(error);
28883
+ throw translateError(error);
28884
+ }
28885
+ });
28886
+ }
28887
+ __name(registerServiceImageTools, "registerServiceImageTools");
28888
+
28889
+ // src/tools/creatives.ts
28890
+ import { z as z115 } from "zod";
28007
28891
  var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
28008
28892
  var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
28009
28893
  var generateImageCreativesGuidance = getProductMakerCapabilityModelGuidance("generate_image_creatives");
@@ -28014,72 +28898,72 @@ var CreativeRefs = {
28014
28898
  images: ImagesInput,
28015
28899
  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.'),
28016
28900
  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.'),
28017
- price: z112.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.'),
28018
- offer: z112.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.'),
28019
- compareAtPrice: z112.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
28020
- productContext: z112.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."),
28021
- productLabel: z112.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.'),
28022
- buyerPersona: z112.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.'),
28023
- isCombo: z112.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
28024
- competitorScanEnabled: z112.boolean().optional().describe("Opcional. Default false: omitirlo significa SIN estudio de mercado. Antes de crear, llama estimate_market_study_cost, muestra al usuario el costo con y sin estudio y preg\xFAntale si lo quiere; env\xEDa true solo si lo acepta (o si solicita la narrativa an\xE1lisis de mercado). En video, true analiza anuncios de la competencia y desbloquea la narrativa an\xE1lisis de mercado; si el estudio no encuentra datos suficientes, degrada al planner cl\xE1sico."),
28025
- brandId: z112.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.")
28901
+ price: z115.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.'),
28902
+ offer: z115.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.'),
28903
+ compareAtPrice: z115.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
28904
+ productContext: z115.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."),
28905
+ productLabel: z115.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.'),
28906
+ buyerPersona: z115.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.'),
28907
+ isCombo: z115.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
28908
+ competitorScanEnabled: z115.boolean().optional().describe("Opcional. Default false: omitirlo significa SIN estudio de mercado. Antes de crear, llama estimate_market_study_cost, muestra al usuario el costo con y sin estudio y preg\xFAntale si lo quiere; env\xEDa true solo si lo acepta (o si solicita la narrativa an\xE1lisis de mercado). En video, true analiza anuncios de la competencia y desbloquea la narrativa an\xE1lisis de mercado; si el estudio no encuentra datos suficientes, degrada al planner cl\xE1sico."),
28909
+ brandId: z115.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.")
28026
28910
  };
28027
- var VideoPresenter = z112.discriminatedUnion("mode", [
28028
- z112.object({
28029
- mode: z112.literal("none")
28911
+ var VideoPresenter = z115.discriminatedUnion("mode", [
28912
+ z115.object({
28913
+ mode: z115.literal("none")
28030
28914
  }).strict(),
28031
- z112.object({
28032
- mode: z112.literal("saved_actor"),
28033
- actorId: z112.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
28915
+ z115.object({
28916
+ mode: z115.literal("saved_actor"),
28917
+ actorId: z115.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
28034
28918
  }).strict(),
28035
- z112.object({
28036
- mode: z112.literal("described"),
28037
- description: z112.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
28919
+ z115.object({
28920
+ mode: z115.literal("described"),
28921
+ description: z115.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
28038
28922
  }).strict()
28039
28923
  ]);
28040
- var VideoCreativeSetup = z112.discriminatedUnion("mode", [
28041
- z112.object({
28042
- mode: z112.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
28924
+ var VideoCreativeSetup = z115.discriminatedUnion("mode", [
28925
+ z115.object({
28926
+ mode: z115.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
28043
28927
  }).strict(),
28044
- z112.object({
28045
- mode: z112.literal("guided"),
28928
+ z115.object({
28929
+ mode: z115.literal("guided"),
28046
28930
  videoPreset: VideoPresetEnum.default("auto").describe('Narrativa opcional. Con estudio de mercado no la preguntes: se usa "auto" y el estudio define la estructura. Sin estudio conserva la elecci\xF3n del usuario. Todas las narrativas funcionan en Un solo clip y Multiescena.'),
28047
- buyerPersona: z112.string().trim().min(1).max(360),
28931
+ buyerPersona: z115.string().trim().min(1).max(360),
28048
28932
  animationStyle: AnimationStyleEnum.optional().describe('Opcional. Estilo visual SOLO para presets animados ("pixar_problem" o "pixar_sassy") \u2014 la API lo rechaza con cualquier otro preset. Opciones: "pixar_3d" (default, look 3D de pel\xEDcula animada), "claymation" (plastilina stop-motion artesanal), "wes_anderson" (tableau sim\xE9trico pastel), "sci_fi" (futurista con HUDs y ne\xF3n), "realista" (fotorrealista, fiel al producto). Si el usuario pide un look animado sin nombrar el estilo, omite el campo.'),
28049
28933
  presenter: VideoPresenter,
28050
- creativeDirection: z112.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
28934
+ creativeDirection: z115.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
28051
28935
  }).strict()
28052
28936
  ]);
28053
- var VideoInput = z112.object({
28937
+ var VideoInput = z115.object({
28054
28938
  images: CreativeRefs.images.optional(),
28055
28939
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
28056
- title: z112.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
28057
- primaryAngle: z112.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".'),
28940
+ title: z115.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
28941
+ primaryAngle: z115.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".'),
28058
28942
  language: CreativeRefs.language,
28059
28943
  country: CreativeRefs.country,
28060
- aspectRatio: z112.enum([
28944
+ aspectRatio: z115.enum([
28061
28945
  "9:16",
28062
28946
  "16:9",
28063
28947
  "Auto"
28064
28948
  ]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
28065
28949
  generateAiEdit: AiEditRequestSchema.optional().describe('Opcional. Pide la Edici\xF3n con IA: una pasada que a\xF1ade texto, efectos y m\xFAsica sobre cada video entregado (variante with_ai_edit; se cobra al entregar, por video). Nivel: "economy" (Econ\xF3mica, Sonnet 5.5, m\xE1s barata) o "pro" (Opus 5.5, mejor calidad, ~10 min). true equivale a "pro"; false/omitido = sin edici\xF3n.'),
28066
- durationSeconds: z112.number().int().min(4).max(30).default(15).describe("Opcional. Duraci\xF3n objetivo: Un solo clip Econ\xF3mico o Medio usa 15s; Un solo clip Pro usa 15s o 30s; Multiescena usa 15s o 30s totales. La combinaci\xF3n de formato y calidad determina la ruta y el costo."),
28067
- videoAngleCount: z112.number().int().min(1).max(3).describe("Cantidad de videos a renderizar: 1, 2 o 3. Es una decisi\xF3n de costo obligatoria: preg\xFAntala incluso cuando creativeSetup.mode=automatic."),
28950
+ durationSeconds: z115.number().int().min(4).max(30).default(15).describe("Opcional. Duraci\xF3n objetivo: Un solo clip Econ\xF3mico o Medio usa 15s; Un solo clip Pro usa 15s o 30s; Multiescena usa 15s o 30s totales. La combinaci\xF3n de formato y calidad determina la ruta y el costo."),
28951
+ videoAngleCount: z115.number().int().min(1).max(3).describe("Cantidad de videos a renderizar: 1, 2 o 3. Es una decisi\xF3n de costo obligatoria: preg\xFAntala incluso cuando creativeSetup.mode=automatic."),
28068
28952
  angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "problem_product_cta" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase", "wearable_showcase", "cinematic", "wild_card", "animated_problem_pixar", "sassy_product_pixar", "market" (narrativa an\xE1lisis de mercado: adapta el video a los patrones ganadores estudiados de la competencia; requiere competitorScanEnabled: true). angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. La lista no puede superar videoAngleCount.'),
28069
28953
  creativeSetup: VideoCreativeSetup.describe("Configuraci\xF3n creativa obligatoria. En guided pregunta narrativa, p\xFAblico, presentador y direcci\xF3n antes de confirmar. Usa automatic s\xF3lo si el usuario deleg\xF3 todo expl\xEDcitamente."),
28070
28954
  videoStructure: VideoStructureEnum.default("single_take").describe('Opcional. Formato: "single_take" = Un solo clip (generado completo; puede incluir cambios de plano) o "multi_scene" = Multiescena (varias tomas concatenadas). Default: single_take.'),
28071
28955
  videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
28072
28956
  videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render: "480p" o "720p". Si se omite, se usa 480p; elige 720p expl\xEDcitamente si necesitas m\xE1s nitidez.'),
28073
- features: z112.array(z112.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
28957
+ features: z115.array(z115.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
28074
28958
  price: CreativeRefs.price,
28075
28959
  offer: CreativeRefs.offer,
28076
28960
  productContext: CreativeRefs.productContext,
28077
28961
  productLabel: CreativeRefs.productLabel,
28078
- cta: z112.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.'),
28079
- scriptNotes: z112.string().optional().describe('Opcional. Notas/restricciones para el guion (la IA las usa al redactar). Ej. "No mencionar la marca X", "Tono divertido, no cl\xEDnico", "Mencionar 2x1". NO es el guion literal: creativeSetup.mode=guided activa siempre la revisi\xF3n palabra-por-palabra con get_video_script / approve_video_script.'),
28962
+ cta: z115.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.'),
28963
+ scriptNotes: z115.string().optional().describe('Opcional. Notas/restricciones para el guion (la IA las usa al redactar). Ej. "No mencionar la marca X", "Tono divertido, no cl\xEDnico", "Mencionar 2x1". NO es el guion literal: creativeSetup.mode=guided activa siempre la revisi\xF3n palabra-por-palabra con get_video_script / approve_video_script.'),
28080
28964
  creativeGender: CreativeGenderEnum.optional().describe('Opcional. QUI\xC9N APARECE EN PANTALLA en el video: mujer ("female"), hombre ("male") o ambos ("mixed"). NO es la voz; cuando se fija, la voz se alinea sola con el personaje. M\xE1ndalo en cuanto el usuario diga qui\xE9n debe SALIR en el video, con cualquier redacci\xF3n ("quiero una mujer", "que salga un hombre", "protagonista femenina"): si no lo mandas, el g\xE9nero lo inventa la IA y el usuario puede recibir lo contrario de lo que pidi\xF3 sin ning\xFAn aviso. NO lo mandes cuando hable del P\xDABLICO COMPRADOR ("esto es para hombres", "mi p\xFAblico son mujeres", "va dirigido a mam\xE1s"): eso es a qui\xE9n se le vende, no qui\xE9n act\xFAa, y fijarlo ah\xED pinea un protagonista que nadie pidi\xF3. "mixed" solo cuando pida expl\xEDcitamente ambos, y ten en cuenta que no garantiza una mezcla: no fija ning\xFAn g\xE9nero y cada creativo elige su protagonista. No lo preguntes como paso obligatorio. Si creativeSetup.presenter.mode="saved_actor", el personaje guardado manda sobre este campo.'),
28081
28965
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
28082
- replicateWinnerWhenFound: z112.boolean().optional().describe("Conserva la preferencia de copiar un ganador si existe. false permite estudiar el mercado sin reemplazar un \xE1ngulo por el anuncio ganador. No a\xF1adas una pregunta obligatoria para este campo."),
28966
+ replicateWinnerWhenFound: z115.boolean().optional().describe("Conserva la preferencia de copiar un ganador si existe. false permite estudiar el mercado sin reemplazar un \xE1ngulo por el anuncio ganador. No a\xF1adas una pregunta obligatoria para este campo."),
28083
28967
  brandId: CreativeRefs.brandId
28084
28968
  }).strict();
28085
28969
  var FreeVideoQualityEnum = VideoQualityEnum;
@@ -28097,7 +28981,7 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
28097
28981
  length: 29
28098
28982
  }, (_, index) => index + 2)
28099
28983
  };
28100
- var FreeVideoModelIdEnum = z112.enum([
28984
+ var FreeVideoModelIdEnum = z115.enum([
28101
28985
  "grok-imagine-video-1-5-preview",
28102
28986
  "minimax-h3/reference-to-video",
28103
28987
  "wan/3-0-video",
@@ -28108,66 +28992,66 @@ var FREE_SELECTABLE_MODEL_IDS = [
28108
28992
  "minimax-h3/reference-to-video",
28109
28993
  "wan/3-0-video"
28110
28994
  ];
28111
- var FreeVideoInput = z112.object({
28995
+ var FreeVideoInput = z115.object({
28112
28996
  images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
28113
- rawPrompt: z112.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."),
28997
+ rawPrompt: z115.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."),
28114
28998
  language: CreativeRefs.language,
28115
28999
  quality: FreeVideoQualityEnum.describe('Calidad del video: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan).'),
28116
29000
  // Retired in MCP 7.0.0 (Task 11): kept in the schema, accepted, and
28117
29001
  // parsed ONLY so the handler can reject it with a message naming its
28118
29002
  // replacement — dropping it silently would turn a caller's explicit
28119
29003
  // Pro++ pick into a quiet fallback to Wan.
28120
- freeProModel: z112.enum([
29004
+ freeProModel: z115.enum([
28121
29005
  "wan",
28122
29006
  "seedance"
28123
29007
  ]).optional().describe("ELIMINADO en MCP 7.0.0 \u2014 usa `modelId`. Enviar este campo produce un error."),
28124
29008
  modelId: FreeVideoModelIdEnum.optional().describe('Opcional. Elige expl\xEDcitamente el modelo del registro en vez del default de la calidad: "grok-imagine-video-1-5-preview" (Grok 1.5), "minimax-h3/reference-to-video" (H3 Max) o "wan/3-0-video" (Wan 3.0). Om\xEDtelo para usar el modelo por defecto de la calidad elegida. El modelo determina la duraci\xF3n permitida (Grok 6/8/10/15s, H3 Max 4-15s, Wan 2-30s) y el precio. "bytedance/seedance-2-5" est\xE1 inactivo y se rechaza con un error claro, nunca un 500.'),
28125
- durationSeconds: z112.number().int().min(2).max(30).describe("Duraci\xF3n exacta. Econ\xF3mico: 6, 8, 10 o 15s; Medio: enteros de 4 a 15s; Pro Wan: 2 a 30s; Pro++ Seedance: 4 a 30s."),
29009
+ durationSeconds: z115.number().int().min(2).max(30).describe("Duraci\xF3n exacta. Econ\xF3mico: 6, 8, 10 o 15s; Medio: enteros de 4 a 15s; Pro Wan: 2 a 30s; Pro++ Seedance: 4 a 30s."),
28126
29010
  videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
28127
- generateAiMusic: z112.boolean().default(false).describe('Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false. Equivale a generateAiEdit (nivel "pro"): la Edici\xF3n con IA trae su propia m\xFAsica.'),
29011
+ generateAiMusic: z115.boolean().default(false).describe('Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false. Equivale a generateAiEdit (nivel "pro"): la Edici\xF3n con IA trae su propia m\xFAsica.'),
28128
29012
  generateAiEdit: AiEditRequestSchema.optional().describe('Opcional. Pide la Edici\xF3n con IA: una pasada que a\xF1ade texto, gr\xE1ficos y m\xFAsica sobre el video ya entregado (variante with_ai_edit; se cobra al entregar). Nivel: "economy" (Econ\xF3mica, Sonnet 5.5, m\xE1s barata) o "pro" (Opus 5.5, mejor calidad, ~10 min). true equivale a "pro". Default sin edici\xF3n.'),
28129
- addSubtitles: z112.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
28130
- idempotencyKey: z112.string().uuid().optional().describe("Opcional. Se acepta por compatibilidad pero se ignora: la identidad del reintento se deriva autom\xE1ticamente de los argumentos sem\xE1nticos, as\xED que un retry con los mismos argumentos ya es idempotente sin este campo.")
29013
+ addSubtitles: z115.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
29014
+ idempotencyKey: z115.string().uuid().optional().describe("Opcional. Se acepta por compatibilidad pero se ignora: la identidad del reintento se deriva autom\xE1ticamente de los argumentos sem\xE1nticos, as\xED que un retry con los mismos argumentos ya es idempotente sin este campo.")
28131
29015
  }).strict();
28132
29016
  var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
28133
- var ImageCreativeSetup = z112.discriminatedUnion("mode", [
28134
- z112.object({
28135
- mode: z112.literal("automatic")
29017
+ var ImageCreativeSetup = z115.discriminatedUnion("mode", [
29018
+ z115.object({
29019
+ mode: z115.literal("automatic")
28136
29020
  }).strict(),
28137
- z112.object({
28138
- mode: z112.literal("guided"),
28139
- creativeArchetype: z112.union([
29021
+ z115.object({
29022
+ mode: z115.literal("guided"),
29023
+ creativeArchetype: z115.union([
28140
29024
  ImageCreativeArchetypeEnum,
28141
- z112.literal("ai_decides")
29025
+ z115.literal("ai_decides")
28142
29026
  ]),
28143
- visualTreatment: z112.union([
29027
+ visualTreatment: z115.union([
28144
29028
  ImageCreativeVisualTreatmentEnum,
28145
- z112.literal("ai_decides")
29029
+ z115.literal("ai_decides")
28146
29030
  ]),
28147
29031
  aspectRatio: ImageCreativeAspectRatioEnum,
28148
- nVariants: z112.union([
28149
- z112.literal(1),
28150
- z112.literal(2),
28151
- z112.literal(3),
28152
- z112.literal(4),
28153
- z112.literal(6)
29032
+ nVariants: z115.union([
29033
+ z115.literal(1),
29034
+ z115.literal(2),
29035
+ z115.literal(3),
29036
+ z115.literal(4),
29037
+ z115.literal(6)
28154
29038
  ]),
28155
- imageModel: z112.enum(PUBLIC_IMAGE_QUALITY_INPUTS).describe('Calidad: "image:1k" (Est\xE1ndar) o "image:2k" (Premium). Los tokens gpt-image-2-5:{1k,2k} y gpt-image-2:{1k,2k} se siguen aceptando.'),
28156
- styleMix: z112.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
28157
- generateAdCopy: z112.boolean(),
28158
- competitorScanEnabled: z112.boolean(),
28159
- productContext: z112.string().trim().min(1).max(3e3).nullable(),
28160
- creativeDirection: z112.string().trim().min(1).max(500).nullable(),
28161
- brandColors: z112.string().trim().min(1).max(240).nullable().describe('Colores en lenguaje natural, igual que en el chat web. Ej. "Azul y Dorado". null significa sin colores espec\xEDficos.'),
28162
- headline: z112.string().trim().min(1).max(120).nullable(),
28163
- subheadline: z112.string().trim().min(1).max(160).nullable()
29039
+ imageModel: z115.enum(PUBLIC_IMAGE_QUALITY_INPUTS).describe('Calidad: "image:1k" (Est\xE1ndar) o "image:2k" (Premium). Los tokens gpt-image-2-5:{1k,2k} y gpt-image-2:{1k,2k} se siguen aceptando.'),
29040
+ styleMix: z115.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
29041
+ generateAdCopy: z115.boolean(),
29042
+ competitorScanEnabled: z115.boolean(),
29043
+ productContext: z115.string().trim().min(1).max(3e3).nullable(),
29044
+ creativeDirection: z115.string().trim().min(1).max(500).nullable(),
29045
+ brandColors: z115.string().trim().min(1).max(240).nullable().describe('Colores en lenguaje natural, igual que en el chat web. Ej. "Azul y Dorado". null significa sin colores espec\xEDficos.'),
29046
+ headline: z115.string().trim().min(1).max(120).nullable(),
29047
+ subheadline: z115.string().trim().min(1).max(160).nullable()
28164
29048
  }).strict()
28165
29049
  ]).superRefine((setup, context) => {
28166
29050
  if (setup.mode !== "guided") return;
28167
29051
  const needsStyleMix = setup.nVariants >= 2 && setup.nVariants <= 4;
28168
29052
  if (needsStyleMix && setup.styleMix === null) {
28169
29053
  context.addIssue({
28170
- code: z112.ZodIssueCode.custom,
29054
+ code: z115.ZodIssueCode.custom,
28171
29055
  path: [
28172
29056
  "styleMix"
28173
29057
  ],
@@ -28176,7 +29060,7 @@ var ImageCreativeSetup = z112.discriminatedUnion("mode", [
28176
29060
  }
28177
29061
  if (!needsStyleMix && setup.styleMix !== null) {
28178
29062
  context.addIssue({
28179
- code: z112.ZodIssueCode.custom,
29063
+ code: z115.ZodIssueCode.custom,
28180
29064
  path: [
28181
29065
  "styleMix"
28182
29066
  ],
@@ -28185,7 +29069,7 @@ var ImageCreativeSetup = z112.discriminatedUnion("mode", [
28185
29069
  }
28186
29070
  if (imageResolutionOf(setup.imageModel) === "2k" && setup.aspectRatio === "4:5") {
28187
29071
  context.addIssue({
28188
- code: z112.ZodIssueCode.custom,
29072
+ code: z115.ZodIssueCode.custom,
28189
29073
  path: [
28190
29074
  "aspectRatio"
28191
29075
  ],
@@ -28193,55 +29077,55 @@ var ImageCreativeSetup = z112.discriminatedUnion("mode", [
28193
29077
  });
28194
29078
  }
28195
29079
  });
28196
- var LandingCreativeSetup = z112.discriminatedUnion("mode", [
28197
- z112.object({
28198
- mode: z112.literal("automatic")
29080
+ var LandingCreativeSetup = z115.discriminatedUnion("mode", [
29081
+ z115.object({
29082
+ mode: z115.literal("automatic")
28199
29083
  }).strict(),
28200
- z112.object({
28201
- mode: z112.literal("guided"),
29084
+ z115.object({
29085
+ mode: z115.literal("guided"),
28202
29086
  landingStrategy: LandingStrategyEnum,
28203
29087
  landingImageFormat: LandingImageFormatEnum,
28204
- competitorScanEnabled: z112.boolean(),
28205
- paymentMethods: z112.string().trim().min(1).max(240).nullable(),
28206
- productContext: z112.string().trim().min(1).max(3e3).nullable(),
28207
- creativeDirection: z112.string().trim().min(1).max(500).nullable(),
28208
- headline: z112.string().trim().min(1).max(120).nullable(),
28209
- subheadline: z112.string().trim().min(1).max(160).nullable()
29088
+ competitorScanEnabled: z115.boolean(),
29089
+ paymentMethods: z115.string().trim().min(1).max(240).nullable(),
29090
+ productContext: z115.string().trim().min(1).max(3e3).nullable(),
29091
+ creativeDirection: z115.string().trim().min(1).max(500).nullable(),
29092
+ headline: z115.string().trim().min(1).max(120).nullable(),
29093
+ subheadline: z115.string().trim().min(1).max(160).nullable()
28210
29094
  }).strict()
28211
29095
  ]);
28212
- var ImageInput = z112.object({
29096
+ var ImageInput = z115.object({
28213
29097
  images: CreativeRefs.images.optional(),
28214
29098
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
28215
- productTitle: z112.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
28216
- primaryAngle: z112.string().min(1).optional().describe('Opcional. \xC1ngulo de venta principal. Si se omite, ProductMaker lo genera y creativeSetup.mode="guided" lo muestra para revisi\xF3n antes de crear las im\xE1genes.'),
29099
+ productTitle: z115.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
29100
+ primaryAngle: z115.string().min(1).optional().describe('Opcional. \xC1ngulo de venta principal. Si se omite, ProductMaker lo genera y creativeSetup.mode="guided" lo muestra para revisi\xF3n antes de crear las im\xE1genes.'),
28217
29101
  buyerPersona: CreativeRefs.buyerPersona,
28218
29102
  language: CreativeRefs.language,
28219
29103
  country: CreativeRefs.country,
28220
29104
  creativeSetup: ImageCreativeSetup.describe("Configuraci\xF3n obligatoria equivalente al wizard web. Usa guided salvo que el usuario delegue expl\xEDcitamente todo; presenta las decisiones guiadas en un \xFAnico checklist compacto."),
28221
29105
  imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
28222
- features: z112.array(z112.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
29106
+ features: z115.array(z115.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
28223
29107
  price: CreativeRefs.price,
28224
- omitPrice: z112.boolean().optional().describe("Pon true \xFAnicamente cuando el usuario pida expl\xEDcitamente una imagen sin precio. Evita que el backend reutilice un precio extra\xEDdo del producto. No lo combines con price ni compareAtPrice."),
29108
+ omitPrice: z115.boolean().optional().describe("Pon true \xFAnicamente cuando el usuario pida expl\xEDcitamente una imagen sin precio. Evita que el backend reutilice un precio extra\xEDdo del producto. No lo combines con price ni compareAtPrice."),
28225
29109
  compareAtPrice: CreativeRefs.compareAtPrice,
28226
29110
  offer: CreativeRefs.offer,
28227
29111
  productLabel: CreativeRefs.productLabel,
28228
29112
  isCombo: CreativeRefs.isCombo,
28229
29113
  brandId: CreativeRefs.brandId
28230
29114
  });
28231
- var EditImageCreativeInput = z112.object({
28232
- taskId: z112.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
28233
- variantIndex: z112.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
28234
- clientRequestId: z112.string().min(1).max(160).optional().describe("Identidad estable de esta correcci\xF3n; reutil\xEDzala al reconciliar una respuesta perdida, no para otra instrucci\xF3n."),
28235
- instruction: z112.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".')
29115
+ var EditImageCreativeInput = z115.object({
29116
+ taskId: z115.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
29117
+ variantIndex: z115.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
29118
+ clientRequestId: z115.string().min(1).max(160).optional().describe("Identidad estable de esta correcci\xF3n; reutil\xEDzala al reconciliar una respuesta perdida, no para otra instrucci\xF3n."),
29119
+ instruction: z115.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".')
28236
29120
  });
28237
- var LandingInput = z112.object({
28238
- includeInitialLandingGif: z112.boolean().optional().describe("Opcional. En nuevas landings cl\xE1sicas habilitadas, omitirlo incluye el primer GIF del producto y su coste en el total. Env\xEDa false si el usuario lo excluye: no se genera ni se reserva su coste. Conserva la elecci\xF3n al confirmar y al reintentar."),
29121
+ var LandingInput = z115.object({
29122
+ includeInitialLandingGif: z115.boolean().optional().describe("Opcional. En nuevas landings cl\xE1sicas habilitadas, omitirlo incluye el primer GIF del producto y su coste en el total. Env\xEDa false si el usuario lo excluye: no se genera ni se reserva su coste. Conserva la elecci\xF3n al confirmar y al reintentar."),
28239
29123
  images: CreativeRefs.images.optional(),
28240
29124
  winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
28241
29125
  language: CreativeRefs.language,
28242
29126
  country: CreativeRefs.country,
28243
29127
  creativeSetup: LandingCreativeSetup.describe("Configuraci\xF3n obligatoria de landing cl\xE1sica equivalente al wizard web. No expone landing editable/code mientras siga siendo admin-only."),
28244
- salesAngle: z112.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.'),
29128
+ salesAngle: z115.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.'),
28245
29129
  buyerPersona: CreativeRefs.buyerPersona,
28246
29130
  price: CreativeRefs.price,
28247
29131
  compareAtPrice: CreativeRefs.compareAtPrice,
@@ -28523,8 +29407,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
28523
29407
  server.registerTool("get_image_edit_status", {
28524
29408
  description: imageEditStatusGuidance.summary,
28525
29409
  inputSchema: {
28526
- taskId: z112.string().uuid(),
28527
- operationId: z112.string().uuid()
29410
+ taskId: z115.string().uuid(),
29411
+ operationId: z115.string().uuid()
28528
29412
  },
28529
29413
  annotations: READS_INTERNAL
28530
29414
  }, async (input) => {
@@ -28581,10 +29465,10 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
28581
29465
  __name(registerCreativeTools, "registerCreativeTools");
28582
29466
 
28583
29467
  // src/tools/creative-variants.ts
28584
- import { z as z113 } from "zod";
29468
+ import { z as z116 } from "zod";
28585
29469
  var estimateCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("estimate_creative_variant");
28586
29470
  var createCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("create_creative_variant");
28587
- var LandingSectionKeyEnum = z113.enum([
29471
+ var LandingSectionKeyEnum = z116.enum([
28588
29472
  "problem_hook",
28589
29473
  "problem_cost",
28590
29474
  "solution_mechanism",
@@ -28624,24 +29508,24 @@ var LandingSectionKeyEnum = z113.enum([
28624
29508
  "lifestyle_fit",
28625
29509
  "join_lifestyle"
28626
29510
  ]);
28627
- var CreativeVariantKindEnum = z113.enum([
29511
+ var CreativeVariantKindEnum = z116.enum([
28628
29512
  "video",
28629
29513
  "image",
28630
29514
  "landing"
28631
29515
  ]);
28632
- var EstimateCreativeVariantInputBase = z113.object({
28633
- sourceTaskId: z113.string().uuid().describe("ID de la tarea original completada."),
29516
+ var EstimateCreativeVariantInputBase = z116.object({
29517
+ sourceTaskId: z116.string().uuid().describe("ID de la tarea original completada."),
28634
29518
  kind: CreativeVariantKindEnum,
28635
29519
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
28636
29520
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
28637
- nVariants: z113.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
29521
+ nVariants: z116.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
28638
29522
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
28639
- instructions: z113.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.")
29523
+ instructions: z116.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.")
28640
29524
  }).strict();
28641
29525
  var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
28642
29526
  if (input.kind === "video" && input.nVariants !== void 0) {
28643
29527
  ctx.addIssue({
28644
- code: z113.ZodIssueCode.custom,
29528
+ code: z116.ZodIssueCode.custom,
28645
29529
  path: [
28646
29530
  "nVariants"
28647
29531
  ],
@@ -28651,7 +29535,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
28651
29535
  if (input.kind !== "video") {
28652
29536
  if (input.videoPreset !== void 0) {
28653
29537
  ctx.addIssue({
28654
- code: z113.ZodIssueCode.custom,
29538
+ code: z116.ZodIssueCode.custom,
28655
29539
  path: [
28656
29540
  "videoPreset"
28657
29541
  ],
@@ -28660,7 +29544,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
28660
29544
  }
28661
29545
  if (input.videoResolution !== void 0) {
28662
29546
  ctx.addIssue({
28663
- code: z113.ZodIssueCode.custom,
29547
+ code: z116.ZodIssueCode.custom,
28664
29548
  path: [
28665
29549
  "videoResolution"
28666
29550
  ],
@@ -28670,7 +29554,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
28670
29554
  }
28671
29555
  if (input.kind !== "image" && input.nVariants !== void 0) {
28672
29556
  ctx.addIssue({
28673
- code: z113.ZodIssueCode.custom,
29557
+ code: z116.ZodIssueCode.custom,
28674
29558
  path: [
28675
29559
  "nVariants"
28676
29560
  ],
@@ -28680,7 +29564,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
28680
29564
  if (input.kind !== "landing") {
28681
29565
  if (input.sectionKey !== void 0) {
28682
29566
  ctx.addIssue({
28683
- code: z113.ZodIssueCode.custom,
29567
+ code: z116.ZodIssueCode.custom,
28684
29568
  path: [
28685
29569
  "sectionKey"
28686
29570
  ],
@@ -28689,7 +29573,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
28689
29573
  }
28690
29574
  if (input.instructions !== void 0) {
28691
29575
  ctx.addIssue({
28692
- code: z113.ZodIssueCode.custom,
29576
+ code: z116.ZodIssueCode.custom,
28693
29577
  path: [
28694
29578
  "instructions"
28695
29579
  ],
@@ -28698,31 +29582,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
28698
29582
  }
28699
29583
  }
28700
29584
  });
28701
- var CreateCreativeVariantInputBase = z113.object({
28702
- sourceTaskId: z113.string().uuid().describe("ID de la tarea original completada."),
29585
+ var CreateCreativeVariantInputBase = z116.object({
29586
+ sourceTaskId: z116.string().uuid().describe("ID de la tarea original completada."),
28703
29587
  kind: CreativeVariantKindEnum,
28704
- confirm: z113.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
28705
- clientRequestId: z113.string().uuid().describe("UUID estable para esta intenci\xF3n (obligatorio por compatibilidad de contrato; el servidor deriva la identidad real del request desde los argumentos sem\xE1nticos, as\xED que un UUID nuevo en un retry NO crea una variante duplicada). Si la API devuelve status=error, una nueva generaci\xF3n exige otra confirmaci\xF3n."),
28706
- angle: z113.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
28707
- buyerPersona: z113.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
28708
- scriptNotes: z113.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
28709
- creativeDirection: z113.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
29588
+ confirm: z116.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
29589
+ clientRequestId: z116.string().uuid().describe("UUID estable para esta intenci\xF3n (obligatorio por compatibilidad de contrato; el servidor deriva la identidad real del request desde los argumentos sem\xE1nticos, as\xED que un UUID nuevo en un retry NO crea una variante duplicada). Si la API devuelve status=error, una nueva generaci\xF3n exige otra confirmaci\xF3n."),
29590
+ angle: z116.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
29591
+ buyerPersona: z116.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
29592
+ scriptNotes: z116.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
29593
+ creativeDirection: z116.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
28710
29594
  videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
28711
29595
  videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
28712
- generateAiMusic: z113.boolean().optional().describe("Opcional para video."),
28713
- nVariants: z113.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
29596
+ generateAiMusic: z116.boolean().optional().describe("Opcional para video."),
29597
+ nVariants: z116.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
28714
29598
  imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
28715
29599
  creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
28716
29600
  visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
28717
29601
  aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
28718
- styleMix: z113.boolean().optional().describe("Opcional para imagen."),
29602
+ styleMix: z116.boolean().optional().describe("Opcional para imagen."),
28719
29603
  sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
28720
- instructions: z113.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.")
29604
+ instructions: z116.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.")
28721
29605
  }).strict();
28722
29606
  var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
28723
29607
  if ((input.kind === "video" || input.kind === "image") && !input.angle) {
28724
29608
  ctx.addIssue({
28725
- code: z113.ZodIssueCode.custom,
29609
+ code: z116.ZodIssueCode.custom,
28726
29610
  path: [
28727
29611
  "angle"
28728
29612
  ],
@@ -28731,7 +29615,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
28731
29615
  }
28732
29616
  if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
28733
29617
  ctx.addIssue({
28734
- code: z113.ZodIssueCode.custom,
29618
+ code: z116.ZodIssueCode.custom,
28735
29619
  path: [
28736
29620
  "instructions"
28737
29621
  ],
@@ -28777,7 +29661,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
28777
29661
  for (const field of fieldsByKind[input.kind]) {
28778
29662
  if (input[field] !== void 0) {
28779
29663
  ctx.addIssue({
28780
- code: z113.ZodIssueCode.custom,
29664
+ code: z116.ZodIssueCode.custom,
28781
29665
  path: [
28782
29666
  field
28783
29667
  ],
@@ -28910,13 +29794,13 @@ function registerCreativeVariantTools(server, api) {
28910
29794
  __name(registerCreativeVariantTools, "registerCreativeVariantTools");
28911
29795
 
28912
29796
  // src/tools/script-review.ts
28913
- import { z as z114 } from "zod";
29797
+ import { z as z117 } from "zod";
28914
29798
  var getVideoScriptGuidance = getProductMakerCapabilityModelGuidance("get_video_script");
28915
29799
  var regenerateVideoScriptGuidance = getProductMakerCapabilityModelGuidance("regenerate_video_script");
28916
29800
  var approveVideoScriptGuidance = getProductMakerCapabilityModelGuidance("approve_video_script");
28917
- var GetVideoScriptInput = z114.object({
28918
- taskId: z114.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
28919
- angleKey: z114.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.')
29801
+ var GetVideoScriptInput = z117.object({
29802
+ taskId: z117.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
29803
+ angleKey: z117.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.')
28920
29804
  }).strict();
28921
29805
  var ApproveVideoScriptInput = ApproveVideoScriptCapabilityRequest;
28922
29806
  var RegenerateVideoScriptInput = RegenerateVideoScriptCapabilityRequest.extend({
@@ -28999,7 +29883,7 @@ function registerScriptReviewTools(server, api) {
28999
29883
  __name(registerScriptReviewTools, "registerScriptReviewTools");
29000
29884
 
29001
29885
  // src/tools/connections.ts
29002
- import { z as z115 } from "zod";
29886
+ import { z as z118 } from "zod";
29003
29887
  var listShopifyShopsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_shops");
29004
29888
  var listShopifyProductsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_products");
29005
29889
  var listMetaAdAccountsGuidance = getProductMakerCapabilityModelGuidance("list_meta_ad_accounts");
@@ -29008,7 +29892,7 @@ var listTikTokAdvertisersGuidance = getProductMakerCapabilityModelGuidance("list
29008
29892
  function registerConnectionTools(server, api) {
29009
29893
  server.registerTool("list_shopify_shops", {
29010
29894
  description: listShopifyShopsGuidance.summary + " Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.",
29011
- inputSchema: z115.object({}).shape,
29895
+ inputSchema: z118.object({}).shape,
29012
29896
  annotations: READS_INTERNAL
29013
29897
  }, async () => {
29014
29898
  try {
@@ -29020,10 +29904,10 @@ function registerConnectionTools(server, api) {
29020
29904
  });
29021
29905
  server.registerTool("list_shopify_products", {
29022
29906
  description: listShopifyProductsGuidance.summary + ' Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.',
29023
- inputSchema: z115.object({
29024
- shopDomain: z115.string().min(3).max(120),
29025
- query: z115.string().trim().max(120).optional(),
29026
- taskId: z115.string().uuid().optional()
29907
+ inputSchema: z118.object({
29908
+ shopDomain: z118.string().min(3).max(120),
29909
+ query: z118.string().trim().max(120).optional(),
29910
+ taskId: z118.string().uuid().optional()
29027
29911
  }).shape,
29028
29912
  annotations: READS_EXTERNAL
29029
29913
  }, async ({ shopDomain, query, taskId }) => {
@@ -29041,7 +29925,7 @@ function registerConnectionTools(server, api) {
29041
29925
  });
29042
29926
  server.registerTool("list_meta_ad_accounts", {
29043
29927
  description: listMetaAdAccountsGuidance.summary + " Para publicar, prefiere `list_meta_destinations`, que tambi\xE9n devuelve el portfolio y las p\xE1ginas disponibles. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.",
29044
- inputSchema: z115.object({}).shape,
29928
+ inputSchema: z118.object({}).shape,
29045
29929
  annotations: READS_EXTERNAL
29046
29930
  }, async () => {
29047
29931
  try {
@@ -29053,7 +29937,7 @@ function registerConnectionTools(server, api) {
29053
29937
  });
29054
29938
  server.registerTool("list_meta_destinations", {
29055
29939
  description: listMetaDestinationsGuidance.summary + " Es obligatorio llamar esta herramienta antes de publicar, mostrar las opciones y enviar al publicador el `portfolioId`, `adAccountId` y `pageId` elegidos; nunca elijas silenciosamente la primera p\xE1gina o cuenta. No modifica Meta.",
29056
- inputSchema: z115.object({}).shape,
29940
+ inputSchema: z118.object({}).shape,
29057
29941
  annotations: READS_EXTERNAL,
29058
29942
  _meta: {
29059
29943
  "openai/widgetAccessible": true
@@ -29068,7 +29952,7 @@ function registerConnectionTools(server, api) {
29068
29952
  });
29069
29953
  server.registerTool("list_tiktok_advertisers", {
29070
29954
  description: listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.",
29071
- inputSchema: z115.object({}).shape,
29955
+ inputSchema: z118.object({}).shape,
29072
29956
  annotations: READS_EXTERNAL
29073
29957
  }, async () => {
29074
29958
  try {
@@ -29082,7 +29966,7 @@ function registerConnectionTools(server, api) {
29082
29966
  __name(registerConnectionTools, "registerConnectionTools");
29083
29967
 
29084
29968
  // src/tools/actors.ts
29085
- import { z as z116 } from "zod";
29969
+ import { z as z119 } from "zod";
29086
29970
  var listActorsGuidance = getProductMakerCapabilityModelGuidance("list_actors");
29087
29971
  var actorVoiceGuidance = {
29088
29972
  capabilities: getProductMakerCapabilityModelGuidance("get_actor_voice_capabilities"),
@@ -29122,7 +30006,7 @@ function sanitizeVoiceResult(value) {
29122
30006
  __name(sanitizeVoiceResult, "sanitizeVoiceResult");
29123
30007
  function registerActorTools(server, api) {
29124
30008
  const actor = {
29125
- actorId: z116.string().min(1).max(64)
30009
+ actorId: z119.string().min(1).max(64)
29126
30010
  };
29127
30011
  const safe = /* @__PURE__ */ __name(async (request) => {
29128
30012
  try {
@@ -29151,8 +30035,8 @@ function registerActorTools(server, api) {
29151
30035
  inputSchema: {
29152
30036
  ...actor,
29153
30037
  draft: ActorVoiceDraftSchema,
29154
- requestKey: z116.string().trim().min(1).max(128),
29155
- acceptedQuoteId: z116.string().trim().min(1).max(128)
30038
+ requestKey: z119.string().trim().min(1).max(128),
30039
+ acceptedQuoteId: z119.string().trim().min(1).max(128)
29156
30040
  },
29157
30041
  annotations: CREATES_PAID
29158
30042
  }, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/previews`, body)));
@@ -29160,7 +30044,7 @@ function registerActorTools(server, api) {
29160
30044
  description: actorVoiceGuidance.get.summary,
29161
30045
  inputSchema: {
29162
30046
  ...actor,
29163
- candidateId: z116.string().min(1).max(64)
30047
+ candidateId: z119.string().min(1).max(64)
29164
30048
  },
29165
30049
  annotations: READS_INTERNAL
29166
30050
  }, async ({ actorId, candidateId }) => safe(api.getJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/previews/${encodeURIComponent(candidateId)}`)));
@@ -29168,15 +30052,15 @@ function registerActorTools(server, api) {
29168
30052
  description: actorVoiceGuidance.approve.summary,
29169
30053
  inputSchema: {
29170
30054
  ...actor,
29171
- candidateId: z116.string().min(1).max(64),
29172
- expectedRevision: z116.number().int().positive(),
29173
- expectedApprovedRevision: z116.number().int().positive().nullable().optional()
30055
+ candidateId: z119.string().min(1).max(64),
30056
+ expectedRevision: z119.number().int().positive(),
30057
+ expectedApprovedRevision: z119.number().int().positive().nullable().optional()
29174
30058
  },
29175
30059
  annotations: CREATES_INTERNAL
29176
30060
  }, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/approve`, body)));
29177
30061
  server.registerTool("list_actors", {
29178
30062
  description: listActorsGuidance.summary + " Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz privada del personaje. Cuando el personaje tiene una voz personalizada, `voice.identityId` y `voice.revision` identifican la versi\xF3n aprobada \u2014 conserva la revisi\xF3n al llamar `approve_actor_voice` (expectedApprovedRevision) para reemplazarla. Los IDs de proveedor nunca se exponen por MCP. 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.",
29179
- inputSchema: z116.object({}).shape,
30063
+ inputSchema: z119.object({}).shape,
29180
30064
  annotations: READS_INTERNAL
29181
30065
  }, async () => {
29182
30066
  try {
@@ -29209,7 +30093,7 @@ function registerActorTools(server, api) {
29209
30093
  __name(registerActorTools, "registerActorTools");
29210
30094
 
29211
30095
  // src/tools/publish.ts
29212
- import { z as z117 } from "zod";
30096
+ import { z as z120 } from "zod";
29213
30097
 
29214
30098
  // src/tools/meta-publish-attempt-id.ts
29215
30099
  var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
@@ -29248,40 +30132,40 @@ var publishToShopifyGuidance = getProductMakerCapabilityModelGuidance("publish_t
29248
30132
  var publishToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_to_meta");
29249
30133
  var publishCreativesToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_creatives_to_meta");
29250
30134
  var publishToTikTokGuidance = getProductMakerCapabilityModelGuidance("publish_to_tiktok");
29251
- var ShopifyInput = z117.object({
29252
- taskId: z117.string().min(1),
29253
- shopDomain: z117.string().min(1),
30135
+ var ShopifyInput = z120.object({
30136
+ taskId: z120.string().min(1),
30137
+ shopDomain: z120.string().min(1),
29254
30138
  /** Override: number of landing images to include. Default = ALL images the
29255
30139
  * task generated (the MCP fetches /v1/results first to count). Bounded
29256
30140
  * generously here; the backend validates each index against
29257
30141
  * landing.images.length and rejects out-of-range. */
29258
- numLandingImages: z117.number().int().min(1).max(20).optional(),
29259
- variantIndex: z117.number().int().min(0).optional(),
30142
+ numLandingImages: z120.number().int().min(1).max(20).optional(),
30143
+ variantIndex: z120.number().int().min(0).optional(),
29260
30144
  /** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
29261
30145
  * When true, the landing renders variant-picker pills; when false, it ships
29262
30146
  * a single-option product page. Defaults to false. */
29263
- showVariantPicker: z117.boolean().optional(),
30147
+ showVariantPicker: z120.boolean().optional(),
29264
30148
  /** Default `auto` updates the product previously published for this task and
29265
30149
  * shop. Use `existing` with targetProductId to choose another product, or
29266
30150
  * `new` only when the user explicitly wants a duplicate/new listing. */
29267
- publicationMode: z117.enum([
30151
+ publicationMode: z120.enum([
29268
30152
  "auto",
29269
30153
  "existing",
29270
30154
  "new"
29271
30155
  ]).optional(),
29272
30156
  /** Shopify product GID, numeric id, or admin product URL. Required when
29273
30157
  * publicationMode is `existing`. */
29274
- targetProductId: z117.string().trim().min(1).max(300).optional(),
30158
+ targetProductId: z120.string().trim().min(1).max(300).optional(),
29275
30159
  /** Add an announcement bar text above add-to-cart (optional). */
29276
- announcementBarEnabled: z117.boolean().optional(),
29277
- announcementBarText: z117.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
30160
+ announcementBarEnabled: z120.boolean().optional(),
30161
+ announcementBarText: z120.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
29278
30162
  commerceCorrection: PublishCommerceCorrectionSchema.optional().describe("Complete seller-confirmed commerce values, with the revision and fingerprint returned in recovery. Never invent missing prices or convert currency."),
29279
- confirmed: z117.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
30163
+ confirmed: z120.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
29280
30164
  });
29281
30165
  var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value, ctx) => {
29282
30166
  if (value.commerceCorrection && value.confirmed !== true) {
29283
30167
  ctx.addIssue({
29284
- code: z117.ZodIssueCode.custom,
30168
+ code: z120.ZodIssueCode.custom,
29285
30169
  path: [
29286
30170
  "confirmed"
29287
30171
  ],
@@ -29290,7 +30174,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
29290
30174
  }
29291
30175
  if (value.announcementBarEnabled && !value.announcementBarText) {
29292
30176
  ctx.addIssue({
29293
- code: z117.ZodIssueCode.custom,
30177
+ code: z120.ZodIssueCode.custom,
29294
30178
  path: [
29295
30179
  "announcementBarText"
29296
30180
  ],
@@ -29299,7 +30183,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
29299
30183
  }
29300
30184
  if (!value.announcementBarEnabled && value.announcementBarText) {
29301
30185
  ctx.addIssue({
29302
- code: z117.ZodIssueCode.custom,
30186
+ code: z120.ZodIssueCode.custom,
29303
30187
  path: [
29304
30188
  "announcementBarText"
29305
30189
  ],
@@ -29313,91 +30197,91 @@ function parseShopifyInput(rawInput) {
29313
30197
  return parsed.data;
29314
30198
  }
29315
30199
  __name(parseShopifyInput, "parseShopifyInput");
29316
- var McpCampaignConfig = z117.object({
30200
+ var McpCampaignConfig = z120.object({
29317
30201
  /** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
29318
30202
  * converts to the account currency server-side. Do NOT pre-convert: on a COP
29319
30203
  * account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
29320
- dailyBudgetUsd: z117.number().positive().optional(),
30204
+ dailyBudgetUsd: z120.number().positive().optional(),
29321
30205
  /** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
29322
30206
  * normalizes at validation). Meta accepts max 1 (single-country ad set).
29323
30207
  * TikTok takes only the first entry. */
29324
- targetCountries: z117.array(CountryIso2Schema).max(1).optional(),
29325
- campaignName: z117.string().trim().min(1).max(100).optional()
30208
+ targetCountries: z120.array(CountryIso2Schema).max(1).optional(),
30209
+ campaignName: z120.string().trim().min(1).max(100).optional()
29326
30210
  });
29327
30211
  var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
29328
- var MetaInput = z117.object({
29329
- taskId: z117.string().min(1),
30212
+ var MetaInput = z120.object({
30213
+ taskId: z120.string().min(1),
29330
30214
  /** Exact Meta Business portfolio selected from `list_meta_destinations`. */
29331
- portfolioId: z117.string().trim().min(1),
30215
+ portfolioId: z120.string().trim().min(1),
29332
30216
  /** Exact Facebook Page selected from `list_meta_destinations`. */
29333
- pageId: z117.string().trim().min(1),
30217
+ pageId: z120.string().trim().min(1),
29334
30218
  adAccountId: McpAdAccountId,
29335
- pixelId: z117.string().min(1).optional(),
29336
- landingUrl: z117.string().url().optional(),
30219
+ pixelId: z120.string().min(1).optional(),
30220
+ landingUrl: z120.string().url().optional(),
29337
30221
  /** Select which angles to publish by 0-based index. Omit to publish all
29338
30222
  * successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
29339
- angleIndexes: z117.array(z117.number().int().nonnegative()).optional(),
30223
+ angleIndexes: z120.array(z120.number().int().nonnegative()).optional(),
29340
30224
  campaignConfig: McpCampaignConfig.optional(),
29341
30225
  /** Override the ad's primary text (body copy). If omitted, ProductMaker
29342
30226
  * uses the auto-generated copy attached to the task. Capped at 500 chars
29343
30227
  * (Meta's primary-text limit). */
29344
- primaryText: z117.string().trim().min(1).max(500).optional(),
30228
+ primaryText: z120.string().trim().min(1).max(500).optional(),
29345
30229
  /** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
29346
30230
  * (wa.me) instead of a landing page. */
29347
- phoneNumber: z117.string().min(8).max(32).optional()
30231
+ phoneNumber: z120.string().min(8).max(32).optional()
29348
30232
  });
29349
- var GroupedTaskSource = z117.object({
29350
- type: z117.literal("task"),
29351
- taskId: z117.string().min(1),
29352
- angleIndex: z117.number().int().min(0).optional(),
29353
- videoVariant: z117.enum([
30233
+ var GroupedTaskSource = z120.object({
30234
+ type: z120.literal("task"),
30235
+ taskId: z120.string().min(1),
30236
+ angleIndex: z120.number().int().min(0).optional(),
30237
+ videoVariant: z120.enum([
29354
30238
  "with_subtitles",
29355
30239
  "without_subtitles"
29356
30240
  ]).optional(),
29357
- imageVariantIndex: z117.number().int().min(0).optional()
29358
- });
29359
- var GroupedMediaSource = z117.object({
29360
- type: z117.literal("media"),
29361
- url: z117.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
29362
- path: z117.string().min(1).optional().describe('Local file path only when this MCP runs over stdio. On the hosted MCP, call request_media_upload and then use source.type="asset".'),
29363
- thumbnailUrl: z117.string().url().optional(),
29364
- thumbnailPath: z117.string().min(1).optional()
30241
+ imageVariantIndex: z120.number().int().min(0).optional()
30242
+ });
30243
+ var GroupedMediaSource = z120.object({
30244
+ type: z120.literal("media"),
30245
+ url: z120.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
30246
+ path: z120.string().min(1).optional().describe('Local file path only when this MCP runs over stdio. On the hosted MCP, call request_media_upload and then use source.type="asset".'),
30247
+ thumbnailUrl: z120.string().url().optional(),
30248
+ thumbnailPath: z120.string().min(1).optional()
29365
30249
  }).refine((value) => Boolean(value.url) !== Boolean(value.path), {
29366
30250
  message: "media source needs exactly one of `url` or `path`"
29367
30251
  }).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
29368
30252
  message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
29369
30253
  });
29370
- var GroupedUploadedAssetSource = z117.object({
29371
- type: z117.literal("asset"),
30254
+ var GroupedUploadedAssetSource = z120.object({
30255
+ type: z120.literal("asset"),
29372
30256
  /** Returned by check_media_upload. ProductMaker resolves ownership server-side. */
29373
- assetId: z117.string().uuid(),
29374
- thumbnailAssetId: z117.string().uuid().optional()
30257
+ assetId: z120.string().uuid(),
30258
+ thumbnailAssetId: z120.string().uuid().optional()
29375
30259
  });
29376
- var GroupedCreative = z117.object({
29377
- type: z117.enum([
30260
+ var GroupedCreative = z120.object({
30261
+ type: z120.enum([
29378
30262
  "image",
29379
30263
  "video"
29380
30264
  ]),
29381
- source: z117.union([
30265
+ source: z120.union([
29382
30266
  GroupedTaskSource,
29383
30267
  GroupedMediaSource,
29384
30268
  GroupedUploadedAssetSource
29385
30269
  ]),
29386
- title: z117.string().max(120).optional(),
29387
- description: z117.string().max(500).optional(),
30270
+ title: z120.string().max(120).optional(),
30271
+ description: z120.string().max(500).optional(),
29388
30272
  cta: MetaCtaType.optional(),
29389
- creativeId: z117.string().min(1).optional()
30273
+ creativeId: z120.string().min(1).optional()
29390
30274
  });
29391
- var GroupedCampaignConfigBase = z117.object({
30275
+ var GroupedCampaignConfigBase = z120.object({
29392
30276
  campaignType: MetaCampaignType.default("sales"),
29393
30277
  optimizationMode: MetaOptimizationMode.default("advantage_plus"),
29394
30278
  budgetMode: MetaBudgetMode.optional(),
29395
- campaignDailyBudget: z117.number().int().positive().optional(),
29396
- adSets: z117.array(z117.object({
29397
- tempId: z117.string().min(1).max(80),
29398
- name: z117.string().min(1).max(100),
29399
- dailyBudget: z117.number().int().positive().optional(),
29400
- creativeIds: z117.array(z117.string().min(1)).min(1)
30279
+ campaignDailyBudget: z120.number().int().positive().optional(),
30280
+ adSets: z120.array(z120.object({
30281
+ tempId: z120.string().min(1).max(80),
30282
+ name: z120.string().min(1).max(100),
30283
+ dailyBudget: z120.number().int().positive().optional(),
30284
+ creativeIds: z120.array(z120.string().min(1)).min(1)
29401
30285
  })).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
29402
30286
  /**
29403
30287
  * Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
@@ -29405,17 +30289,17 @@ var GroupedCampaignConfigBase = z117.object({
29405
30289
  * opaque `invalid_literal` and made the WhatsApp destination — supported by
29406
30290
  * the API since #849 — unreachable from this tool.
29407
30291
  */
29408
- destination: z117.discriminatedUnion("type", [
29409
- z117.object({
29410
- type: z117.literal("landing"),
29411
- landingUrl: z117.string().url()
30292
+ destination: z120.discriminatedUnion("type", [
30293
+ z120.object({
30294
+ type: z120.literal("landing"),
30295
+ landingUrl: z120.string().url()
29412
30296
  }),
29413
- z117.object({
29414
- type: z117.literal("whatsapp"),
29415
- phoneNumber: z117.string().min(8).max(32)
30297
+ z120.object({
30298
+ type: z120.literal("whatsapp"),
30299
+ phoneNumber: z120.string().min(8).max(32)
29416
30300
  })
29417
30301
  ]),
29418
- saveAsDefault: z117.boolean().default(false),
30302
+ saveAsDefault: z120.boolean().default(false),
29419
30303
  budgetPolicy: MetaBudgetPolicy.optional()
29420
30304
  });
29421
30305
  function resolveBudgetMode(value) {
@@ -29429,7 +30313,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
29429
30313
  const budgetMode = resolveBudgetMode(value);
29430
30314
  if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
29431
30315
  ctx.addIssue({
29432
- code: z117.ZodIssueCode.custom,
30316
+ code: z120.ZodIssueCode.custom,
29433
30317
  path: [
29434
30318
  "campaignDailyBudget"
29435
30319
  ],
@@ -29440,7 +30324,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
29440
30324
  value.adSets.forEach((adSet, index) => {
29441
30325
  if (adSet.dailyBudget === void 0) {
29442
30326
  ctx.addIssue({
29443
- code: z117.ZodIssueCode.custom,
30327
+ code: z120.ZodIssueCode.custom,
29444
30328
  path: [
29445
30329
  "adSets",
29446
30330
  index,
@@ -29455,28 +30339,28 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
29455
30339
  ...value,
29456
30340
  budgetMode: resolveBudgetMode(value)
29457
30341
  }));
29458
- var PublishCreativesToMetaInputBase = z117.object({
29459
- creatives: z117.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
30342
+ var PublishCreativesToMetaInputBase = z120.object({
30343
+ creatives: z120.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
29460
30344
  /** Exact Meta Business portfolio selected from `list_meta_destinations`. */
29461
- portfolioId: z117.string().trim().min(1),
30345
+ portfolioId: z120.string().trim().min(1),
29462
30346
  /** Exact Facebook Page selected from `list_meta_destinations`. */
29463
- pageId: z117.string().trim().min(1),
30347
+ pageId: z120.string().trim().min(1),
29464
30348
  adAccountId: McpAdAccountId,
29465
- pixelId: z117.string().min(1).optional(),
30349
+ pixelId: z120.string().min(1).optional(),
29466
30350
  /** Required unless `campaignConfig.destination` is whatsapp — see the
29467
30351
  * superRefine below. Mirrors the API's `FacebookCreateCampaignFromTasksBodySchema`. */
29468
- landingUrl: z117.string().url().optional(),
30352
+ landingUrl: z120.string().url().optional(),
29469
30353
  /** ISO-2 code or resolvable country name — mirrors the API contract
29470
30354
  * (CountryIso2Schema), which normalizes names to alpha-2 at validation. */
29471
30355
  targetCountry: CountryIso2Schema,
29472
- campaignName: z117.string().trim().min(1).max(100),
30356
+ campaignName: z120.string().trim().min(1).max(100),
29473
30357
  campaignConfig: GroupedCampaignConfig.optional()
29474
30358
  });
29475
30359
  var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
29476
30360
  const destination = value.campaignConfig?.destination;
29477
30361
  if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl) {
29478
30362
  ctx.addIssue({
29479
- code: z117.ZodIssueCode.custom,
30363
+ code: z120.ZodIssueCode.custom,
29480
30364
  path: [
29481
30365
  "landingUrl"
29482
30366
  ],
@@ -29485,7 +30369,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
29485
30369
  }
29486
30370
  if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
29487
30371
  ctx.addIssue({
29488
- code: z117.ZodIssueCode.custom,
30372
+ code: z120.ZodIssueCode.custom,
29489
30373
  path: [
29490
30374
  "campaignConfig",
29491
30375
  "destination",
@@ -29504,16 +30388,16 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
29504
30388
  }
29505
30389
  return value;
29506
30390
  });
29507
- var PresignUploadResponseSchema = z117.object({
29508
- uploadUrl: z117.string().url().refine((value) => {
30391
+ var PresignUploadResponseSchema = z120.object({
30392
+ uploadUrl: z120.string().url().refine((value) => {
29509
30393
  const url = new URL(value);
29510
30394
  return !url.username && !url.password && url.protocol === "https:";
29511
30395
  }, "presigned upload URL must be HTTPS without embedded credentials"),
29512
- publicUrl: z117.string().url().refine((value) => {
30396
+ publicUrl: z120.string().url().refine((value) => {
29513
30397
  const url = new URL(value);
29514
30398
  return !url.username && !url.password;
29515
30399
  }, "public media URL must not contain embedded credentials"),
29516
- stagingAssetId: z117.string().uuid()
30400
+ stagingAssetId: z120.string().uuid()
29517
30401
  });
29518
30402
  async function uploadResolvedMedia(api, media, assetRole) {
29519
30403
  if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
@@ -29538,16 +30422,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
29538
30422
  return uploadResolvedMedia(api, media, assetRole);
29539
30423
  }
29540
30424
  __name(resolveAndUploadMedia, "resolveAndUploadMedia");
29541
- var TikTokInput = z117.object({
29542
- taskId: z117.string().min(1),
29543
- advertiserId: z117.string().min(1),
29544
- pixelId: z117.string().min(1).optional(),
29545
- landingUrl: z117.string().url().optional(),
29546
- angleIndexes: z117.array(z117.number().int().nonnegative()).optional(),
30425
+ var TikTokInput = z120.object({
30426
+ taskId: z120.string().min(1),
30427
+ advertiserId: z120.string().min(1),
30428
+ pixelId: z120.string().min(1).optional(),
30429
+ landingUrl: z120.string().url().optional(),
30430
+ angleIndexes: z120.array(z120.number().int().nonnegative()).optional(),
29547
30431
  campaignConfig: McpCampaignConfig.optional(),
29548
30432
  /** Override the ad's description (TikTok's body copy). If omitted,
29549
30433
  * ProductMaker uses the auto-generated copy attached to the task. */
29550
- primaryText: z117.string().trim().min(1).max(500).optional()
30434
+ primaryText: z120.string().trim().min(1).max(500).optional()
29551
30435
  });
29552
30436
  function registerPublishTools(server, api, opts = {}) {
29553
30437
  const transport = opts.transport ?? "http";
@@ -29899,39 +30783,39 @@ __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
29899
30783
 
29900
30784
  // src/tools/meta-upload.ts
29901
30785
  import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
29902
- import { z as z118 } from "zod";
30786
+ import { z as z121 } from "zod";
29903
30787
  var requestMediaUploadGuidance = getProductMakerCapabilityModelGuidance("request_media_upload");
29904
- var UploadSessionInputBase = z118.object({
29905
- mediaKind: z118.enum([
30788
+ var UploadSessionInputBase = z121.object({
30789
+ mediaKind: z121.enum([
29906
30790
  "image",
29907
30791
  "video",
29908
30792
  "mixed"
29909
30793
  ]).default("mixed").describe("The kind of file the user will select. Use image for product references and landing pages, video for a single video, and mixed only when the consumer supports both."),
29910
- assetRole: z118.enum([
30794
+ assetRole: z121.enum([
29911
30795
  "media",
29912
30796
  "thumbnail"
29913
30797
  ]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail."),
29914
- uploadPurpose: z118.enum([
30798
+ uploadPurpose: z121.enum([
29915
30799
  "product_reference",
29916
30800
  "creative",
29917
30801
  "generic"
29918
30802
  ]).default("generic").describe("The user's purpose for these files. Use product_reference for a product photo used by tasks or landings, creative for media that will become an ad or creative, and generic only when neither applies."),
29919
- maxFiles: z118.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
30803
+ maxFiles: z121.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
29920
30804
  });
29921
- var OpenAiFileReference = z118.object({
29922
- download_url: z118.string().url().describe("Required by ChatGPT's openai/fileParams schema. The App prefers getFileDownloadUrl(file_id) for a fresh URL, and the server never fetches this value."),
29923
- file_id: z118.string().min(1),
29924
- mime_type: z118.string().min(1).optional(),
29925
- file_name: z118.string().min(1).optional()
30805
+ var OpenAiFileReference = z121.object({
30806
+ download_url: z121.string().url().describe("Required by ChatGPT's openai/fileParams schema. The App prefers getFileDownloadUrl(file_id) for a fresh URL, and the server never fetches this value."),
30807
+ file_id: z121.string().min(1),
30808
+ mime_type: z121.string().min(1).optional(),
30809
+ file_name: z121.string().min(1).optional()
29926
30810
  });
29927
30811
  var UploadSessionToolInputShape = {
29928
30812
  ...UploadSessionInputBase.shape,
29929
- files: z118.array(OpenAiFileReference).max(10).optional().describe("Optional OpenAI-hosted file references provided by ChatGPT. They are only metadata for the UI bridge and are never forwarded to ProductMaker.")
30813
+ files: z121.array(OpenAiFileReference).max(10).optional().describe("Optional OpenAI-hosted file references provided by ChatGPT. They are only metadata for the UI bridge and are never forwarded to ProductMaker.")
29930
30814
  };
29931
- var UploadSessionToolInput = z118.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
30815
+ var UploadSessionToolInput = z121.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
29932
30816
  if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
29933
30817
  ctx.addIssue({
29934
- code: z118.ZodIssueCode.custom,
30818
+ code: z121.ZodIssueCode.custom,
29935
30819
  path: [
29936
30820
  "mediaKind"
29937
30821
  ],
@@ -29940,7 +30824,7 @@ var UploadSessionToolInput = z118.object(UploadSessionToolInputShape).superRefin
29940
30824
  }
29941
30825
  if (value.mediaKind === "video" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
29942
30826
  ctx.addIssue({
29943
- code: z118.ZodIssueCode.custom,
30827
+ code: z121.ZodIssueCode.custom,
29944
30828
  path: [
29945
30829
  "maxFiles"
29946
30830
  ],
@@ -29949,7 +30833,7 @@ var UploadSessionToolInput = z118.object(UploadSessionToolInputShape).superRefin
29949
30833
  }
29950
30834
  if (value.mediaKind === "image" && (value.maxFiles ?? 1) > 5) {
29951
30835
  ctx.addIssue({
29952
- code: z118.ZodIssueCode.custom,
30836
+ code: z121.ZodIssueCode.custom,
29953
30837
  path: [
29954
30838
  "maxFiles"
29955
30839
  ],
@@ -29957,38 +30841,38 @@ var UploadSessionToolInput = z118.object(UploadSessionToolInputShape).superRefin
29957
30841
  });
29958
30842
  }
29959
30843
  });
29960
- var UploadSessionResponse = z118.object({
29961
- sessionId: z118.string().uuid(),
29962
- uploadPageUrl: z118.string().url(),
29963
- mediaKind: z118.enum([
30844
+ var UploadSessionResponse = z121.object({
30845
+ sessionId: z121.string().uuid(),
30846
+ uploadPageUrl: z121.string().url(),
30847
+ mediaKind: z121.enum([
29964
30848
  "image",
29965
30849
  "video",
29966
30850
  "mixed"
29967
30851
  ]),
29968
- assetRole: z118.enum([
30852
+ assetRole: z121.enum([
29969
30853
  "media",
29970
30854
  "thumbnail"
29971
30855
  ]),
29972
- maxFiles: z118.number().int().positive(),
29973
- expiresAt: z118.string().datetime()
30856
+ maxFiles: z121.number().int().positive(),
30857
+ expiresAt: z121.string().datetime()
29974
30858
  });
29975
- var UploadStatusInput = z118.object({
29976
- sessionId: z118.string().uuid().describe("The sessionId returned by request_media_upload.")
30859
+ var UploadStatusInput = z121.object({
30860
+ sessionId: z121.string().uuid().describe("The sessionId returned by request_media_upload.")
29977
30861
  });
29978
- var UniversalUploadStatusResponse = z118.discriminatedUnion("status", [
29979
- z118.object({
29980
- status: z118.literal("pending"),
29981
- assets: z118.array(z118.never()).length(0)
30862
+ var UniversalUploadStatusResponse = z121.discriminatedUnion("status", [
30863
+ z121.object({
30864
+ status: z121.literal("pending"),
30865
+ assets: z121.array(z121.never()).length(0)
29982
30866
  }),
29983
- z118.object({
29984
- status: z118.literal("uploaded"),
29985
- assets: z118.array(z118.object({
29986
- assetId: z118.string().uuid(),
29987
- mediaKind: z118.enum([
30867
+ z121.object({
30868
+ status: z121.literal("uploaded"),
30869
+ assets: z121.array(z121.object({
30870
+ assetId: z121.string().uuid(),
30871
+ mediaKind: z121.enum([
29988
30872
  "image",
29989
30873
  "video"
29990
30874
  ]),
29991
- assetRole: z118.enum([
30875
+ assetRole: z121.enum([
29992
30876
  "media",
29993
30877
  "thumbnail"
29994
30878
  ])
@@ -29996,13 +30880,13 @@ var UniversalUploadStatusResponse = z118.discriminatedUnion("status", [
29996
30880
  })
29997
30881
  ]);
29998
30882
  var UniversalUploadStructuredResponse = UploadSessionResponse.extend({
29999
- uploadPurpose: z118.enum([
30883
+ uploadPurpose: z121.enum([
30000
30884
  "product_reference",
30001
30885
  "creative",
30002
30886
  "generic"
30003
30887
  ]),
30004
- checkToolName: z118.literal("check_media_upload"),
30005
- resolveEndpoint: z118.string().url()
30888
+ checkToolName: z121.literal("check_media_upload"),
30889
+ resolveEndpoint: z121.string().url()
30006
30890
  });
30007
30891
  function contextualizeUploadPageUrl(uploadPageUrl, uploadPurpose) {
30008
30892
  const url = new URL(uploadPageUrl);
@@ -30093,15 +30977,15 @@ function registerMediaUploadTools(server, api) {
30093
30977
  __name(registerMediaUploadTools, "registerMediaUploadTools");
30094
30978
 
30095
30979
  // src/tools/campaigns.ts
30096
- import { z as z119 } from "zod";
30980
+ import { z as z122 } from "zod";
30097
30981
  var getMetaCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_meta_campaign_status");
30098
30982
  var getTikTokCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_tiktok_campaign_status");
30099
- var MetaStatusInput = z119.object({
30100
- campaignId: z119.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
30983
+ var MetaStatusInput = z122.object({
30984
+ campaignId: z122.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
30101
30985
  });
30102
- var TikTokStatusInput = z119.object({
30103
- campaignId: z119.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
30104
- advertiserId: z119.string().min(1).describe("The same advertiserId used to publish the campaign.")
30986
+ var TikTokStatusInput = z122.object({
30987
+ campaignId: z122.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
30988
+ advertiserId: z122.string().min(1).describe("The same advertiserId used to publish the campaign.")
30105
30989
  });
30106
30990
  function registerCampaignTools(server, api) {
30107
30991
  server.registerTool("get_meta_campaign_status", {
@@ -30206,10 +31090,10 @@ function renderTikTokStatusMarkdown(r) {
30206
31090
  __name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
30207
31091
 
30208
31092
  // ../../packages/product-economics/src/product-costing.ts
30209
- import { z as z121 } from "zod";
31093
+ import { z as z124 } from "zod";
30210
31094
 
30211
31095
  // ../../packages/product-economics/src/product-costing-countries.ts
30212
- import { z as z120 } from "zod";
31096
+ import { z as z123 } from "zod";
30213
31097
 
30214
31098
  // ../../packages/product-economics/src/product-costing-shipping-references.json
30215
31099
  var product_costing_shipping_references_default = {
@@ -30273,21 +31157,21 @@ var product_costing_shipping_references_default = {
30273
31157
  };
30274
31158
 
30275
31159
  // ../../packages/product-economics/src/product-costing-countries.ts
30276
- var ShippingReferenceSchema = z120.object({
30277
- reference: z120.number().positive(),
30278
- min: z120.number().nonnegative(),
30279
- max: z120.number().positive(),
30280
- updatedAt: z120.string().date(),
30281
- confidence: z120.enum([
31160
+ var ShippingReferenceSchema = z123.object({
31161
+ reference: z123.number().positive(),
31162
+ min: z123.number().nonnegative(),
31163
+ max: z123.number().positive(),
31164
+ updatedAt: z123.string().date(),
31165
+ confidence: z123.enum([
30282
31166
  "high",
30283
31167
  "medium",
30284
31168
  "medium-low"
30285
31169
  ]),
30286
- sources: z120.array(z120.string().url().startsWith("https://")).min(1).max(3)
31170
+ sources: z123.array(z123.string().url().startsWith("https://")).min(1).max(3)
30287
31171
  }).superRefine(({ reference, min, max }, context) => {
30288
31172
  if (min > reference || reference > max) {
30289
31173
  context.addIssue({
30290
- code: z120.ZodIssueCode.custom,
31174
+ code: z123.ZodIssueCode.custom,
30291
31175
  message: "shipping_reference_outside_range",
30292
31176
  path: [
30293
31177
  "reference"
@@ -30295,7 +31179,7 @@ var ShippingReferenceSchema = z120.object({
30295
31179
  });
30296
31180
  }
30297
31181
  });
30298
- var SHIPPING_REFERENCES = z120.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
31182
+ var SHIPPING_REFERENCES = z123.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
30299
31183
  var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
30300
31184
  var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
30301
31185
  "MX",
@@ -30477,18 +31361,18 @@ function getProductCostingCountryName(country) {
30477
31361
  __name(getProductCostingCountryName, "getProductCostingCountryName");
30478
31362
 
30479
31363
  // ../../packages/product-economics/src/product-costing.ts
30480
- var ProductCostingInputSchema = z121.object({
30481
- country: z121.string().refine(isProductCostingCountryCode, "unsupported_country"),
30482
- supplierCost: z121.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
30483
- shippingCost: z121.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
30484
- returnRate: z121.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
30485
- cancellationRate: z121.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
30486
- adsManagerCpa: z121.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
30487
- desiredMargin: z121.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
31364
+ var ProductCostingInputSchema = z124.object({
31365
+ country: z124.string().refine(isProductCostingCountryCode, "unsupported_country"),
31366
+ supplierCost: z124.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
31367
+ shippingCost: z124.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
31368
+ returnRate: z124.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
31369
+ cancellationRate: z124.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
31370
+ adsManagerCpa: z124.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
31371
+ desiredMargin: z124.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
30488
31372
  }).superRefine(({ returnRate, cancellationRate }, context) => {
30489
31373
  if (returnRate + cancellationRate >= 1) {
30490
31374
  context.addIssue({
30491
- code: z121.ZodIssueCode.custom,
31375
+ code: z124.ZodIssueCode.custom,
30492
31376
  message: "delivery_rate_positive",
30493
31377
  path: [
30494
31378
  "cancellationRate"
@@ -30585,15 +31469,15 @@ function calculateProductCosting(rawInput) {
30585
31469
  __name(calculateProductCosting, "calculateProductCosting");
30586
31470
 
30587
31471
  // src/tools/product-costing.ts
30588
- import { z as z122 } from "zod";
31472
+ import { z as z125 } from "zod";
30589
31473
  var costProductGuidance = getProductMakerCapabilityModelGuidance("cost_product");
30590
- var optionalAmount = /* @__PURE__ */ __name((description) => z122.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
30591
- var optionalPercent = /* @__PURE__ */ __name((description) => z122.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
30592
- var ProductCostingToolInput = z122.object({
30593
- country: z122.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
31474
+ var optionalAmount = /* @__PURE__ */ __name((description) => z125.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
31475
+ var optionalPercent = /* @__PURE__ */ __name((description) => z125.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
31476
+ var ProductCostingToolInput = z125.object({
31477
+ country: z125.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
30594
31478
  message: "Pa\xEDs no soportado por ProductMaker."
30595
31479
  }).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
30596
- supplierCost: z122.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."),
31480
+ supplierCost: z125.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."),
30597
31481
  supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
30598
31482
  shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
30599
31483
  shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
@@ -30756,7 +31640,7 @@ function formatPercent(value) {
30756
31640
  __name(formatPercent, "formatPercent");
30757
31641
 
30758
31642
  // src/tools/product-research.ts
30759
- import { z as z123 } from "zod";
31643
+ import { z as z126 } from "zod";
30760
31644
 
30761
31645
  // src/tools/product-research-idempotency.ts
30762
31646
  var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
@@ -31127,17 +32011,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
31127
32011
  ]);
31128
32012
  var POLL_INTERVAL_MS = 5e3;
31129
32013
  var MAX_WAIT_SECONDS = 120;
31130
- var FindInput = z123.object({
32014
+ var FindInput = z126.object({
31131
32015
  targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
31132
32016
  categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
31133
- confirmed: z123.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
31134
- waitSeconds: z123.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
32017
+ confirmed: z126.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
32018
+ waitSeconds: z126.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
31135
32019
  });
31136
- var GetInput = z123.object({
31137
- runId: z123.string().uuid().describe("ID del run devuelto por find_winning_products.")
32020
+ var GetInput = z126.object({
32021
+ runId: z126.string().uuid().describe("ID del run devuelto por find_winning_products.")
31138
32022
  });
31139
- var ListInput = z123.object({
31140
- limit: z123.number().int().min(1).max(20).default(10)
32023
+ var ListInput = z126.object({
32024
+ limit: z126.number().int().min(1).max(20).default(10)
31141
32025
  });
31142
32026
  function textResult(text2) {
31143
32027
  return {
@@ -31276,17 +32160,17 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
31276
32160
  __name(registerProductResearchTools, "registerProductResearchTools");
31277
32161
 
31278
32162
  // src/tools/dropshipping-projects.ts
31279
- import { z as z124 } from "zod";
32163
+ import { z as z127 } from "zod";
31280
32164
  var startDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("start_dropshipping_project");
31281
32165
  var costDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("cost_dropshipping_project");
31282
32166
  var guideDropshippingLaunchGuidance = getProductMakerCapabilityModelGuidance("guide_dropshipping_launch");
31283
- var ProjectId = z124.string().uuid();
31284
- var DropshippingProjectStatus = z124.enum([
32167
+ var ProjectId = z127.string().uuid();
32168
+ var DropshippingProjectStatus = z127.enum([
31285
32169
  "active",
31286
32170
  "completed",
31287
32171
  "archived"
31288
32172
  ]);
31289
- var DropshippingProjectPhase = z124.enum([
32173
+ var DropshippingProjectPhase = z127.enum([
31290
32174
  "research",
31291
32175
  "costing",
31292
32176
  "media",
@@ -31295,21 +32179,21 @@ var DropshippingProjectPhase = z124.enum([
31295
32179
  "advertising",
31296
32180
  "complete"
31297
32181
  ]);
31298
- var DropshippingRewardMilestone = z124.enum([
32182
+ var DropshippingRewardMilestone = z127.enum([
31299
32183
  "product_selected",
31300
32184
  "costing_saved",
31301
32185
  "product_media_ready",
31302
32186
  "creatives_completed",
31303
32187
  "campaign_created"
31304
32188
  ]);
31305
- var TargetCountry = z124.enum([
32189
+ var TargetCountry = z127.enum([
31306
32190
  "CO",
31307
32191
  "MX",
31308
32192
  "PE",
31309
32193
  "EC",
31310
32194
  "CL"
31311
32195
  ]);
31312
- var ResearchCategory = z124.enum([
32196
+ var ResearchCategory = z127.enum([
31313
32197
  "auto",
31314
32198
  "automotive",
31315
32199
  "kitchen",
@@ -31324,7 +32208,7 @@ var ResearchCategory = z124.enum([
31324
32208
  "garden",
31325
32209
  "cleaning"
31326
32210
  ]);
31327
- var HttpUrl3 = z124.string().url().max(2e3).refine((value) => {
32211
+ var HttpUrl3 = z127.string().url().max(2e3).refine((value) => {
31328
32212
  try {
31329
32213
  return [
31330
32214
  "http:",
@@ -31334,45 +32218,45 @@ var HttpUrl3 = z124.string().url().max(2e3).refine((value) => {
31334
32218
  return false;
31335
32219
  }
31336
32220
  }, "URL must use HTTP(S)");
31337
- var ResearchCandidateOrigin = z124.object({
31338
- type: z124.literal("research_candidate"),
32221
+ var ResearchCandidateOrigin = z127.object({
32222
+ type: z127.literal("research_candidate"),
31339
32223
  runId: ProjectId,
31340
- candidateSourceId: z124.string().min(1).max(160)
32224
+ candidateSourceId: z127.string().min(1).max(160)
31341
32225
  });
31342
- var TaskOrigin = z124.object({
31343
- type: z124.literal("task"),
32226
+ var TaskOrigin = z127.object({
32227
+ type: z127.literal("task"),
31344
32228
  taskId: ProjectId
31345
32229
  });
31346
- var ManualOrigin = z124.object({
31347
- type: z124.literal("manual"),
32230
+ var ManualOrigin = z127.object({
32231
+ type: z127.literal("manual"),
31348
32232
  clientRequestId: ProjectId,
31349
- country: z124.string().min(2).max(3).optional()
32233
+ country: z127.string().min(2).max(3).optional()
31350
32234
  });
31351
- var StartProjectInput = z124.object({
31352
- origin: z124.discriminatedUnion("type", [
32235
+ var StartProjectInput = z127.object({
32236
+ origin: z127.discriminatedUnion("type", [
31353
32237
  ResearchCandidateOrigin,
31354
32238
  TaskOrigin,
31355
32239
  ManualOrigin
31356
32240
  ]),
31357
- productName: z124.string().trim().min(1).max(240).optional(),
32241
+ productName: z127.string().trim().min(1).max(240).optional(),
31358
32242
  productUrl: HttpUrl3.optional(),
31359
- country: z124.string().min(2).max(3).optional(),
31360
- languageCode: z124.string().min(2).max(16).optional()
32243
+ country: z127.string().min(2).max(3).optional(),
32244
+ languageCode: z127.string().min(2).max(16).optional()
31361
32245
  }).strict();
31362
- var CostProjectInput = z124.object({
32246
+ var CostProjectInput = z127.object({
31363
32247
  projectId: ProjectId,
31364
- version: z124.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
31365
- country: z124.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
32248
+ version: z127.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
32249
+ country: z127.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
31366
32250
  message: "Pa\xEDs no soportado por ProductMaker."
31367
32251
  }).describe("C\xF3digo ISO-2 del pa\xEDs del proyecto. Usa el `targetCountry` que devuelve el proyecto."),
31368
- supplierCost: z124.number().finite().positive().describe("Costo real de una unidad con el proveedor, en la moneda del pa\xEDs. \xDANICO dato que debes pedirle al usuario: nunca lo inventes."),
31369
- shippingCost: z124.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
31370
- returnRate: z124.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos devueltos (0.25 = 25%). Omite para usar el valor gu\xEDa del pa\xEDs."),
31371
- cancellationRate: z124.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos cancelados (0.15 = 15%). Omite para usar el valor gu\xEDa del pa\xEDs."),
31372
- adsManagerCpa: z124.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
31373
- desiredMargin: z124.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
31374
- });
31375
- var GuideInput = z124.object({
32252
+ supplierCost: z127.number().finite().positive().describe("Costo real de una unidad con el proveedor, en la moneda del pa\xEDs. \xDANICO dato que debes pedirle al usuario: nunca lo inventes."),
32253
+ shippingCost: z127.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
32254
+ returnRate: z127.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos devueltos (0.25 = 25%). Omite para usar el valor gu\xEDa del pa\xEDs."),
32255
+ cancellationRate: z127.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos cancelados (0.15 = 15%). Omite para usar el valor gu\xEDa del pa\xEDs."),
32256
+ adsManagerCpa: z127.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
32257
+ desiredMargin: z127.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
32258
+ });
32259
+ var GuideInput = z127.object({
31376
32260
  projectId: ProjectId.optional()
31377
32261
  }).strict();
31378
32262
  var NEXT_ACTION_KINDS = [
@@ -31390,29 +32274,29 @@ var NEXT_ACTION_KINDS = [
31390
32274
  "repair_meta_campaign",
31391
32275
  "review_paused_campaign"
31392
32276
  ];
31393
- var DropshippingProjectNextAction = z124.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind) => z124.object({
31394
- kind: z124.literal(kind),
31395
- href: z124.string().min(1)
32277
+ var DropshippingProjectNextAction = z127.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind) => z127.object({
32278
+ kind: z127.literal(kind),
32279
+ href: z127.string().min(1)
31396
32280
  })));
31397
- var CostingInputSchema = z124.object({
31398
- country: z124.string().min(2).max(3),
31399
- supplierCost: z124.number().finite().positive(),
31400
- shippingCost: z124.number().finite().nonnegative(),
31401
- returnRate: z124.number().finite().min(0).lt(1),
31402
- cancellationRate: z124.number().finite().min(0).lt(1),
31403
- adsManagerCpa: z124.number().finite().nonnegative(),
31404
- desiredMargin: z124.number().finite().min(0).lt(1)
32281
+ var CostingInputSchema = z127.object({
32282
+ country: z127.string().min(2).max(3),
32283
+ supplierCost: z127.number().finite().positive(),
32284
+ shippingCost: z127.number().finite().nonnegative(),
32285
+ returnRate: z127.number().finite().min(0).lt(1),
32286
+ cancellationRate: z127.number().finite().min(0).lt(1),
32287
+ adsManagerCpa: z127.number().finite().nonnegative(),
32288
+ desiredMargin: z127.number().finite().min(0).lt(1)
31405
32289
  }).superRefine(({ returnRate, cancellationRate }, context) => {
31406
32290
  if (returnRate + cancellationRate >= 1) context.addIssue({
31407
- code: z124.ZodIssueCode.custom,
32291
+ code: z127.ZodIssueCode.custom,
31408
32292
  path: [
31409
32293
  "cancellationRate"
31410
32294
  ]
31411
32295
  });
31412
32296
  });
31413
- var FiniteCostingNumber = z124.number().finite();
31414
- var CostingResultSchema = z124.object({
31415
- country: z124.string().min(2).max(3),
32297
+ var FiniteCostingNumber = z127.number().finite();
32298
+ var CostingResultSchema = z127.object({
32299
+ country: z127.string().min(2).max(3),
31416
32300
  deliveryRate: FiniteCostingNumber,
31417
32301
  adjustedShippingCost: FiniteCostingNumber,
31418
32302
  adjustedCpa: FiniteCostingNumber,
@@ -31425,45 +32309,45 @@ var CostingResultSchema = z124.object({
31425
32309
  estimatedProfit: FiniteCostingNumber,
31426
32310
  actualMargin: FiniteCostingNumber,
31427
32311
  breakEvenAdsManagerCpa: FiniteCostingNumber,
31428
- wasPriceAutoAdjusted: z124.boolean(),
31429
- profitabilityStatus: z124.literal("healthy")
31430
- });
31431
- var RewardProgressSchema = z124.object({
31432
- campaignKey: z124.literal("dropshipping-launch-v1"),
31433
- totalCredits: z124.number().int().positive(),
31434
- earnedCredits: z124.number().int().nonnegative(),
31435
- milestones: z124.array(z124.object({
32312
+ wasPriceAutoAdjusted: z127.boolean(),
32313
+ profitabilityStatus: z127.literal("healthy")
32314
+ });
32315
+ var RewardProgressSchema = z127.object({
32316
+ campaignKey: z127.literal("dropshipping-launch-v1"),
32317
+ totalCredits: z127.number().int().positive(),
32318
+ earnedCredits: z127.number().int().nonnegative(),
32319
+ milestones: z127.array(z127.object({
31436
32320
  milestone: DropshippingRewardMilestone,
31437
- credits: z124.number().int().positive(),
31438
- available: z124.boolean().optional(),
31439
- earned: z124.boolean(),
32321
+ credits: z127.number().int().positive(),
32322
+ available: z127.boolean().optional(),
32323
+ earned: z127.boolean(),
31440
32324
  grantId: ProjectId.nullable(),
31441
- grantedAt: z124.string().datetime().nullable()
32325
+ grantedAt: z127.string().datetime().nullable()
31442
32326
  })).length(DropshippingRewardMilestone.options.length)
31443
32327
  });
31444
- var ProjectResponseSchema = z124.object({
32328
+ var ProjectResponseSchema = z127.object({
31445
32329
  id: ProjectId,
31446
32330
  taskId: ProjectId.nullable(),
31447
32331
  status: DropshippingProjectStatus,
31448
- originKey: z124.string().min(1),
32332
+ originKey: z127.string().min(1),
31449
32333
  researchRunId: ProjectId.nullable(),
31450
- candidateSourceId: z124.string().nullable(),
31451
- productName: z124.string().nullable(),
32334
+ candidateSourceId: z127.string().nullable(),
32335
+ productName: z127.string().nullable(),
31452
32336
  productUrl: HttpUrl3.nullable(),
31453
32337
  referenceImageUrl: HttpUrl3.nullable(),
31454
- targetCountry: z124.string().nullable(),
31455
- languageCode: z124.string().nullable(),
31456
- hasReferenceImage: z124.boolean(),
31457
- hasWinnerReferenceImage: z124.boolean(),
32338
+ targetCountry: z127.string().nullable(),
32339
+ languageCode: z127.string().nullable(),
32340
+ hasReferenceImage: z127.boolean(),
32341
+ hasWinnerReferenceImage: z127.boolean(),
31458
32342
  costingInput: CostingInputSchema.nullable(),
31459
32343
  costingResult: CostingResultSchema.nullable(),
31460
- costingVersion: z124.number().int().positive().nullable(),
31461
- version: z124.number().int().positive(),
32344
+ costingVersion: z127.number().int().positive().nullable(),
32345
+ version: z127.number().int().positive(),
31462
32346
  phase: DropshippingProjectPhase,
31463
32347
  nextAction: DropshippingProjectNextAction.nullable(),
31464
32348
  rewards: RewardProgressSchema,
31465
- createdAt: z124.string().datetime(),
31466
- updatedAt: z124.string().datetime()
32349
+ createdAt: z127.string().datetime(),
32350
+ updatedAt: z127.string().datetime()
31467
32351
  }).passthrough();
31468
32352
  var NEXT_ACTION_GUIDANCE = {
31469
32353
  cost_product: "Llama `cost_dropshipping_project` con el costo real del proveedor. Nunca lo inventes: p\xEDdeselo al usuario. Es el \xDANICO dato que debes pedirle: omite env\xEDo, devoluciones, cancelaciones, CPA y margen para que ProductMaker use los valores gu\xEDa del pa\xEDs, y luego dile al usuario cu\xE1les us\xF3 por si quiere corregirlos.",
@@ -31483,14 +32367,14 @@ var NEXT_ACTION_GUIDANCE = {
31483
32367
  function projectText(raw, referenceValues = []) {
31484
32368
  const project = ProjectResponseSchema.parse(raw);
31485
32369
  const kind = project.nextAction?.kind;
31486
- const guidance2 = kind ? NEXT_ACTION_GUIDANCE[kind] : void 0;
32370
+ const guidance5 = kind ? NEXT_ACTION_GUIDANCE[kind] : void 0;
31487
32371
  const winnerReferenceResult = project.hasWinnerReferenceImage ? WinnerReferenceInput.safeParse({
31488
32372
  runId: project.researchRunId,
31489
32373
  candidateSourceId: project.candidateSourceId
31490
32374
  }) : void 0;
31491
32375
  const winnerReference = winnerReferenceResult?.success ? winnerReferenceResult.data : void 0;
31492
32376
  const standaloneCreativeGuidance = winnerReference ? "Si el usuario pide s\xF3lo crear contenido con este producto, no contin\xFAes el costeo y no pidas el costo del proveedor. Llama `prepare_winning_product_image` con `winnerReference`, muestra la foto y, despu\xE9s de su elecci\xF3n, pasa el mismo `winnerReference` a la herramienta creativa independiente solicitada." : void 0;
31493
- const guidedNextActionGuidance = guidance2 ? `S\xF3lo si el usuario quiere continuar el lanzamiento guiado completo: ${guidance2}` : void 0;
32377
+ const guidedNextActionGuidance = guidance5 ? `S\xF3lo si el usuario quiere continuar el lanzamiento guiado completo: ${guidance5}` : void 0;
31494
32378
  const referenceNotice = referenceValues.length > 0 ? `Valores gu\xEDa de ${project.targetCountry ?? "el pa\xEDs"} que ProductMaker us\xF3 porque no los diste: ${referenceValues.join(", ")}. Son un punto de partida editable, no datos del usuario: dile cu\xE1les fueron y ofr\xE9cele corregirlos. Si te da los suyos, vuelve a llamar cost_dropshipping_project con la version nueva.` : void 0;
31495
32379
  return {
31496
32380
  content: [
@@ -31583,58 +32467,58 @@ function registerDropshippingProjectTools(server, api) {
31583
32467
  __name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
31584
32468
 
31585
32469
  // src/tools/daily-control.ts
31586
- import { z as z125 } from "zod";
32470
+ import { z as z128 } from "zod";
31587
32471
  var getDailyControlGuidance = getProductMakerCapabilityModelGuidance("get_daily_control");
31588
32472
  var linkCampaignToProjectGuidance = getProductMakerCapabilityModelGuidance("link_campaign_to_project");
31589
32473
  var reportDailyReturnsGuidance = getProductMakerCapabilityModelGuidance("report_daily_returns");
31590
- var ProviderCampaignIdentifier = z125.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
31591
- var DateOnly = z125.string().regex(/^\d{4}-\d{2}-\d{2}$/);
31592
- var DailyControlInput = z125.object({
32474
+ var ProviderCampaignIdentifier = z128.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
32475
+ var DateOnly = z128.string().regex(/^\d{4}-\d{2}-\d{2}$/);
32476
+ var DailyControlInput = z128.object({
31593
32477
  date: DateOnly.optional().describe("D\xEDa final de la ventana (YYYY-MM-DD). Si se omite, la ventana termina AYER en UTC \u2014 nunca hoy, porque el d\xEDa en curso est\xE1 incompleto \u2014 y el servidor puede correrla un d\xEDa m\xE1s atr\xE1s si en la zona horaria de la tienda ese d\xEDa todav\xEDa no ha cerrado."),
31594
- days: z125.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
31595
- projectId: z125.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
32478
+ days: z128.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
32479
+ projectId: z128.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
31596
32480
  }).strict();
31597
- var LinkCampaignInput = z125.object({
31598
- projectId: z125.string().uuid(),
31599
- provider: z125.enum([
32481
+ var LinkCampaignInput = z128.object({
32482
+ projectId: z128.string().uuid(),
32483
+ provider: z128.enum([
31600
32484
  "meta",
31601
32485
  "tiktok"
31602
32486
  ]),
31603
32487
  accountId: ProviderCampaignIdentifier.describe("Cuenta publicitaria del proveedor. Para Meta el servidor la IGNORA y deriva la cuenta real de la campa\xF1a \u2014 se sigue pidiendo aqu\xED solo porque TikTok s\xED la necesita para resolver la campa\xF1a."),
31604
32488
  campaignId: ProviderCampaignIdentifier
31605
32489
  }).strict();
31606
- var ReportReturnsInput = z125.object({
31607
- projectId: z125.string().uuid(),
32490
+ var ReportReturnsInput = z128.object({
32491
+ projectId: z128.string().uuid(),
31608
32492
  date: DateOnly,
31609
- returnedOrders: z125.number().int().min(0).max(1e5),
31610
- note: z125.string().trim().max(280).optional()
32493
+ returnedOrders: z128.number().int().min(0).max(1e5),
32494
+ note: z128.string().trim().max(280).optional()
31611
32495
  }).strict();
31612
- var DailyControlStatusMirror = DailyControlStatusEnum.or(z125.string());
31613
- var DailyControlLightMirror = DailyControlLightEnum.or(z125.string());
31614
- var DailyControlSourceMirror = DailyControlSourceEnum.or(z125.string());
31615
- var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z125.string());
31616
- var DailyControlDayMirror = z125.object({
31617
- date: z125.string(),
31618
- returnedOrders: z125.number(),
32496
+ var DailyControlStatusMirror = DailyControlStatusEnum.or(z128.string());
32497
+ var DailyControlLightMirror = DailyControlLightEnum.or(z128.string());
32498
+ var DailyControlSourceMirror = DailyControlSourceEnum.or(z128.string());
32499
+ var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z128.string());
32500
+ var DailyControlDayMirror = z128.object({
32501
+ date: z128.string(),
32502
+ returnedOrders: z128.number(),
31619
32503
  returnedSource: DailyControlSourceMirror,
31620
32504
  cancelledSource: DailyControlSourceMirror,
31621
- hasEstimates: z125.boolean(),
32505
+ hasEstimates: z128.boolean(),
31622
32506
  light: DailyControlLightMirror
31623
32507
  }).passthrough();
31624
- var DailyControlProductMirror = z125.object({
31625
- projectId: z125.string().uuid(),
31626
- productName: z125.string().nullable(),
32508
+ var DailyControlProductMirror = z128.object({
32509
+ projectId: z128.string().uuid(),
32510
+ productName: z128.string().nullable(),
31627
32511
  status: DailyControlStatusMirror,
31628
- statusMessage: z125.string().nullable(),
31629
- days: z125.array(DailyControlDayMirror),
32512
+ statusMessage: z128.string().nullable(),
32513
+ days: z128.array(DailyControlDayMirror),
31630
32514
  verdict: DailyControlVerdictMirror.nullable(),
31631
- rationale: z125.string().nullable()
32515
+ rationale: z128.string().nullable()
31632
32516
  }).passthrough();
31633
- var DailyControlResponseMirror = z125.object({
31634
- since: z125.string(),
31635
- until: z125.string(),
31636
- products: z125.array(DailyControlProductMirror),
31637
- archivedProjectSpend: z125.array(z125.unknown())
32517
+ var DailyControlResponseMirror = z128.object({
32518
+ since: z128.string(),
32519
+ until: z128.string(),
32520
+ products: z128.array(DailyControlProductMirror),
32521
+ archivedProjectSpend: z128.array(z128.unknown())
31638
32522
  }).passthrough();
31639
32523
  var MCP_WARNINGS_KEY = "_mcpDailyControlWarnings";
31640
32524
  function withWarnings(response) {
@@ -31709,7 +32593,7 @@ __name(registerDailyControlTools, "registerDailyControlTools");
31709
32593
  // src/tools/product-control.ts
31710
32594
  import { readFile, stat } from "fs/promises";
31711
32595
  import { basename as basename2, isAbsolute, resolve as resolve3 } from "path";
31712
- import { z as z126 } from "zod";
32596
+ import { z as z129 } from "zod";
31713
32597
  var getProductControlGuidance = getProductMakerCapabilityModelGuidance("get_product_control");
31714
32598
  var listImportableProductsGuidance = getProductMakerCapabilityModelGuidance("list_importable_products");
31715
32599
  var importProductControlsGuidance = getProductMakerCapabilityModelGuidance("import_product_controls");
@@ -31722,36 +32606,36 @@ var createOperationGuidance = getProductMakerCapabilityModelGuidance("create_ope
31722
32606
  var mergeProductsGuidance = getProductMakerCapabilityModelGuidance("merge_products");
31723
32607
  var syncShopifyOrdersGuidance = getProductMakerCapabilityModelGuidance("sync_shopify_orders");
31724
32608
  var importOrdersFileGuidance = getProductMakerCapabilityModelGuidance("import_orders_file");
31725
- var DateOnly2 = z126.string().regex(/^\d{4}-\d{2}-\d{2}$/);
32609
+ var DateOnly2 = z129.string().regex(/^\d{4}-\d{2}-\d{2}$/);
31726
32610
  var IMPORT_BATCH_MAX2 = 50;
31727
- var ProductControlListInput = z126.object({
31728
- days: z126.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
32611
+ var ProductControlListInput = z129.object({
32612
+ days: z129.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
31729
32613
  date: DateOnly2.optional().describe("D\xEDa final de la ventana (YYYY-MM-DD). Si se omite, termina en el \xFAltimo d\xEDa cerrado \u2014 nunca hoy, que est\xE1 incompleto."),
31730
- shopDomain: z126.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
32614
+ shopDomain: z129.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
31731
32615
  }).strict();
31732
- var ImportableProductsInput = z126.object({
31733
- q: z126.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
31734
- after: z126.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
31735
- shopDomain: z126.string().min(1).optional()
32616
+ var ImportableProductsInput = z129.object({
32617
+ q: z129.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
32618
+ after: z129.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
32619
+ shopDomain: z129.string().min(1).optional()
31736
32620
  }).strict();
31737
- var ImportProductControlsInput = z126.object({
31738
- shopifyProductIds: z126.array(z126.string().min(1).max(64)).min(1).max(IMPORT_BATCH_MAX2).describe("Identificadores tal como vinieron de list_importable_products. NO los inventes ni los deduzcas del t\xEDtulo."),
32621
+ var ImportProductControlsInput = z129.object({
32622
+ shopifyProductIds: z129.array(z129.string().min(1).max(64)).min(1).max(IMPORT_BATCH_MAX2).describe("Identificadores tal como vinieron de list_importable_products. NO los inventes ni los deduzcas del t\xEDtulo."),
31739
32623
  // Obligatorio, como en el endpoint: con dos tiendas conectadas, importar
31740
32624
  // sin decir a cuál archivaría el producto bajo la tienda equivocada.
31741
- shopDomain: z126.string().min(1)
32625
+ shopDomain: z129.string().min(1)
31742
32626
  }).strict();
31743
- var SetProductCostingInput = z126.object({
31744
- shopDomain: z126.string().min(1),
31745
- shopifyProductId: z126.string().min(1).max(64),
31746
- targetCountry: z126.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
31747
- supplierCost: z126.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
31748
- shippingCost: z126.number().finite().nonnegative(),
31749
- returnRate: z126.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
31750
- cancellationRate: z126.number().finite().min(0).lt(1)
32627
+ var SetProductCostingInput = z129.object({
32628
+ shopDomain: z129.string().min(1),
32629
+ shopifyProductId: z129.string().min(1).max(64),
32630
+ targetCountry: z129.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
32631
+ supplierCost: z129.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
32632
+ shippingCost: z129.number().finite().nonnegative(),
32633
+ returnRate: z129.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
32634
+ cancellationRate: z129.number().finite().min(0).lt(1)
31751
32635
  }).strict();
31752
- var Count = z126.number().int().min(0).max(1e5).nullable().optional();
31753
- var ReportProductCountsInput = z126.object({
31754
- productControlId: z126.string().uuid().describe("El identificador que devuelve get_product_control."),
32636
+ var Count = z129.number().int().min(0).max(1e5).nullable().optional();
32637
+ var ReportProductCountsInput = z129.object({
32638
+ productControlId: z129.string().uuid().describe("El identificador que devuelve get_product_control."),
31755
32639
  date: DateOnly2,
31756
32640
  orders: Count.describe("Pedidos que entraron ese d\xEDa."),
31757
32641
  cancelledOrders: Count,
@@ -31761,36 +32645,36 @@ var ReportProductCountsInput = z126.object({
31761
32645
  }).strict();
31762
32646
  var MAX_ORDERS_FILE_BYTES = 8 * 1024 * 1024;
31763
32647
  var SyncShopifyOrdersInput = SyncShopifyOrdersSchema;
31764
- var ImportOrdersFileInput = z126.object({
32648
+ var ImportOrdersFileInput = z129.object({
31765
32649
  /** Ruta ABSOLUTA en la máquina de quien usa el MCP. */
31766
- path: z126.string().min(1),
31767
- operationId: z126.string().uuid().optional(),
32650
+ path: z129.string().min(1),
32651
+ operationId: z129.string().uuid().optional(),
31768
32652
  /** Solo mirar: devuelve el resumen y no escribe nada. */
31769
- dryRun: z126.boolean().optional()
32653
+ dryRun: z129.boolean().optional()
31770
32654
  }).strict();
31771
- var CreateOperationInput = z126.object({
31772
- countryCode: z126.string().length(2),
31773
- label: z126.string().trim().min(1).max(60).optional()
32655
+ var CreateOperationInput = z129.object({
32656
+ countryCode: z129.string().length(2),
32657
+ label: z129.string().trim().min(1).max(60).optional()
31774
32658
  }).strict();
31775
- var MergeProductsInput = z126.object({
31776
- sourceId: z126.string().uuid(),
31777
- targetId: z126.string().uuid()
32659
+ var MergeProductsInput = z129.object({
32660
+ sourceId: z129.string().uuid(),
32661
+ targetId: z129.string().uuid()
31778
32662
  }).strict();
31779
- var SetMonthlyExpenseInput = z126.object({
31780
- month: z126.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
32663
+ var SetMonthlyExpenseInput = z129.object({
32664
+ month: z129.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
31781
32665
  label: SaveMonthlyExpenseSchema.shape.label,
31782
- amount: z126.number().finite().min(0),
31783
- operationId: z126.string().uuid().optional()
32666
+ amount: z129.number().finite().min(0),
32667
+ operationId: z129.string().uuid().optional()
31784
32668
  }).strict();
31785
- var SetAdSpendInput = z126.object({
31786
- month: z126.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
31787
- operationId: z126.string().uuid().optional(),
32669
+ var SetAdSpendInput = z129.object({
32670
+ month: z129.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
32671
+ operationId: z129.string().uuid().optional(),
31788
32672
  /** null RETIRA la corrección; cero afirma que no pautó. */
31789
- amount: z126.number().finite().min(0).nullable()
32673
+ amount: z129.number().finite().min(0).nullable()
31790
32674
  }).strict();
31791
- var GetFinancesInput = z126.object({
31792
- month: z126.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional().describe("Mes en YYYY-MM. Si se omite, el mes en curso. Solo mes calendario."),
31793
- operationId: z126.string().uuid().optional().describe("El negocio, si tiene mas de uno. Con uno solo no hace falta decirlo.")
32675
+ var GetFinancesInput = z129.object({
32676
+ month: z129.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional().describe("Mes en YYYY-MM. Si se omite, el mes en curso. Solo mes calendario."),
32677
+ operationId: z129.string().uuid().optional().describe("El negocio, si tiene mas de uno. Con uno solo no hace falta decirlo.")
31794
32678
  }).strict();
31795
32679
  function registerProductControlTools(server, api, { transport = "http" } = {}) {
31796
32680
  server.registerTool("get_product_control", {
@@ -31980,28 +32864,28 @@ function registerProductControlTools(server, api, { transport = "http" } = {}) {
31980
32864
  __name(registerProductControlTools, "registerProductControlTools");
31981
32865
 
31982
32866
  // src/tools/market-study-estimate.ts
31983
- import { z as z127 } from "zod";
32867
+ import { z as z130 } from "zod";
31984
32868
  var estimateMarketStudyGuidance = getProductMakerCapabilityModelGuidance("estimate_market_study_cost");
31985
- var ProductTaskOutputEnum = z127.enum([
32869
+ var ProductTaskOutputEnum = z130.enum([
31986
32870
  "landing",
31987
32871
  "video",
31988
32872
  "image"
31989
32873
  ]);
31990
- var EstimateMarketStudyInput = z127.object({
31991
- kind: z127.enum([
32874
+ var EstimateMarketStudyInput = z130.object({
32875
+ kind: z130.enum([
31992
32876
  "video",
31993
32877
  "product_task"
31994
32878
  ]).describe('"video" para generate_video_creative; "product_task" para create_product_task.'),
31995
- videoAngleCount: z127.number().int().min(1).max(3).describe("Los mismos 1, 2 o 3 videos que vas a pedir al crear."),
31996
- durationSeconds: z127.number().int().min(4).max(30).default(15).describe("La misma duraci\xF3n que enviar\xE1s al crear."),
32879
+ videoAngleCount: z130.number().int().min(1).max(3).describe("Los mismos 1, 2 o 3 videos que vas a pedir al crear."),
32880
+ durationSeconds: z130.number().int().min(4).max(30).default(15).describe("La misma duraci\xF3n que enviar\xE1s al crear."),
31997
32881
  videoStructure: VideoStructureEnum.default("single_take"),
31998
32882
  videoQuality: VideoQualityEnum.default("economy"),
31999
32883
  videoResolution: VideoResolutionEnum.default("480p"),
32000
- productImageCount: z127.number().int().min(1).max(5).optional().describe("Cantidad de fotos del producto que adjuntar\xE1s (1-5)."),
32884
+ productImageCount: z130.number().int().min(1).max(5).optional().describe("Cantidad de fotos del producto que adjuntar\xE1s (1-5)."),
32001
32885
  outputs: ProductTaskOutputEnum.array().min(1).max(3).optional().describe('Solo para kind="product_task": los mismos outputs de create_product_task. Default: todos.'),
32002
- generateAiMusic: z127.boolean().optional(),
32886
+ generateAiMusic: z130.boolean().optional(),
32003
32887
  generateAiEdit: AiEditRequestSchema.optional(),
32004
- replicateWinnerWhenFound: z127.boolean().optional().describe("La misma preferencia que enviar\xE1s al crear. false excluye la reserva de r\xE9plica del ganador de la cotizaci\xF3n con estudio.")
32888
+ replicateWinnerWhenFound: z130.boolean().optional().describe("La misma preferencia que enviar\xE1s al crear. false excluye la reserva de r\xE9plica del ganador de la cotizaci\xF3n con estudio.")
32005
32889
  }).strict();
32006
32890
  function buildMarketStudyEstimateRequests(input) {
32007
32891
  const quotesVideo = input.kind === "video" || (input.outputs ?? [
@@ -32147,6 +33031,9 @@ function createServer(opts) {
32147
33031
  proxyBaseUrl,
32148
33032
  transport
32149
33033
  });
33034
+ registerBrandTools(server, api);
33035
+ registerServiceLandingTools(server, api);
33036
+ registerServiceImageTools(server, api);
32150
33037
  registerAnimatedVideoTools(server, api);
32151
33038
  registerCreativeTools(server, api, {
32152
33039
  transport