@productmaker/mcp 1.16.0 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/http.js +38 -14
  2. package/dist/stdio.js +38 -14
  3. package/package.json +3 -3
package/dist/http.js CHANGED
@@ -2348,7 +2348,7 @@ var require_video = __commonJS({
2348
2348
  Object.defineProperty(exports, "__esModule", {
2349
2349
  value: true
2350
2350
  });
2351
- exports.RegenerateVideoResponse = exports.RegenerateVideoRequest = exports.RegenerateVideoScriptNarrativeResponse = exports.RegenerateVideoScriptNarrativeRequest = exports.ApproveVideoScriptResponse = exports.ApproveVideoScriptRequest = exports.GetVideoScriptForReviewResponse = exports.VideoScriptSceneForReview = exports.VideoGenerator = exports.VideoGeneratorVariant = exports.ScriptReviewStatus = exports.VideoGeneratorStatus = void 0;
2351
+ exports.RegenerateVideoResponse = exports.RegenerateVideoRequest = exports.RegenerateVideoScriptNarrativeResponse = exports.RegenerateVideoScriptNarrativeRequest = exports.ApproveVideoScriptResponse = exports.ApproveVideoScriptRequest = exports.VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS = exports.VIDEO_SCRIPT_APPROVAL_MAX_SCENES = exports.GetVideoScriptForReviewResponse = exports.VideoScriptSceneForReview = exports.VideoGenerator = exports.VideoGeneratorVariant = exports.ScriptReviewStatus = exports.VideoGeneratorStatus = void 0;
2352
2352
  var zod_1 = __require("zod");
2353
2353
  var video_enums_1 = require_video_enums();
2354
2354
  exports.VideoGeneratorStatus = zod_1.z.enum([
@@ -2423,7 +2423,14 @@ var require_video = __commonJS({
2423
2423
  * mute shot. The review UI must NOT block approval on an empty scene when this
2424
2424
  * is set. Absent/false → a spoken beat that must carry dialogue (the approval
2425
2425
  * gate blocks an empty one). Optional for backwards compatibility. */
2426
- silentAllowed: zod_1.z.boolean().optional()
2426
+ silentAllowed: zod_1.z.boolean().optional(),
2427
+ /** Winner scene's persuasive job. Shown beside editable dialogue so edits
2428
+ * preserve why the source beat exists. */
2429
+ persuasiveFunction: zod_1.z.string().optional(),
2430
+ /** Objective visual observed in the winner source scene. */
2431
+ winnerVisual: zod_1.z.string().optional(),
2432
+ /** Localized target visual expected to accompany this spoken line. */
2433
+ expectedVisual: zod_1.z.string().optional()
2427
2434
  });
2428
2435
  exports.GetVideoScriptForReviewResponse = zod_1.z.object({
2429
2436
  status: exports.ScriptReviewStatus,
@@ -2436,13 +2443,15 @@ var require_video = __commonJS({
2436
2443
  * reverts an approved card back to "Revisar" without telling the user why. */
2437
2444
  error: zod_1.z.string().optional()
2438
2445
  });
2446
+ exports.VIDEO_SCRIPT_APPROVAL_MAX_SCENES = 50;
2447
+ exports.VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS = 2e3;
2439
2448
  exports.ApproveVideoScriptRequest = zod_1.z.object({
2440
- angleKey: zod_1.z.string().min(1).optional(),
2449
+ angleKey: zod_1.z.string().trim().min(1).max(120).optional(),
2441
2450
  scenes: zod_1.z.array(zod_1.z.object({
2442
2451
  index: zod_1.z.number().int().nonnegative(),
2443
- dialogue: zod_1.z.string()
2444
- })).min(1)
2445
- });
2452
+ dialogue: zod_1.z.string().trim().min(1).max(exports.VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS)
2453
+ }).strict()).min(1).max(exports.VIDEO_SCRIPT_APPROVAL_MAX_SCENES)
2454
+ }).strict();
2446
2455
  exports.ApproveVideoScriptResponse = zod_1.z.object({
2447
2456
  status: exports.ScriptReviewStatus
2448
2457
  });
@@ -6023,15 +6032,16 @@ var require_pricing = __commonJS({
6023
6032
  // Observed on the gap render 090ce235-55bd-401c-8ae8-ce362f4d7769 (local, the
6024
6033
  // only place these ran — zero prod BillingEntry rows for these labels as of
6025
6034
  // 2026-07-18, so there is no prod p75 to calibrate against yet):
6026
- // gap-panel-fidelity-qa ×5 = 0.700¢ our → 0.14¢ our / 0.28¢ user each
6035
+ // Grok 4.5 HIGH is billed on actual token use. A recent P2 E2E measured
6036
+ // ~0.74¢/panel, so quote a conservative whole-cent ceiling per call.
6027
6037
  // clip-payoff-judge ×2 = 2.638¢ our → 1.32¢ our / 2.64¢ user each
6028
6038
  // ⚠️ OWNER DECISION PENDING: these are provisional ceilings from ONE render.
6029
6039
  // Re-derive from prod BillingEntry once gap_replication has volume.
6030
6040
  // Unit = one per-panel gap fidelity judge (3 images: our panel + the winner
6031
6041
  // keyframe + the product reference). Always on for gap_replication.
6032
6042
  "openrouter:gap-panel-fidelity-qa": {
6033
- ourCostCents: 0.2,
6034
- userPriceCents: 0.4
6043
+ ourCostCents: 1,
6044
+ userPriceCents: 2
6035
6045
  },
6036
6046
  // Unit = one payoff-judge pass over a RENDERED clip (video input → the most
6037
6047
  // expensive of the three). Behind PAYOFF_JUDGE_ENABLED (default OFF).
@@ -6141,6 +6151,18 @@ var require_pricing = __commonJS({
6141
6151
  ourCostCents: 1,
6142
6152
  userPriceCents: 2
6143
6153
  },
6154
+ // P2 post-edit path. These two high-reasoning text calls run only when a user
6155
+ // changes a winner-mapped spoken line: first localized reconciliation, then
6156
+ // an independent whole-plan audit. Both are unmeasured at launch; quote each
6157
+ // at the same conservative ceiling as gap-shot-alignment and bill actual use.
6158
+ "openrouter:gap-script-reconciliation": {
6159
+ ourCostCents: 1,
6160
+ userPriceCents: 2
6161
+ },
6162
+ "openrouter:gap-script-visual-audit": {
6163
+ ourCostCents: 1,
6164
+ userPriceCents: 2
6165
+ },
6144
6166
  // Unit = one ffmpeg mix laying the replicated winner's own audio track under our
6145
6167
  // voiceover, producing the extra `ad_final_with_winner_music` deliverable
6146
6168
  // (gap_replication only, behind GAP_WINNER_MUSIC_ENABLED, default OFF). No AI
@@ -8094,7 +8116,7 @@ var ApiClient = class {
8094
8116
  };
8095
8117
 
8096
8118
  // src/index.ts
8097
- var MCP_VERSION = "1.16.0";
8119
+ var MCP_VERSION = "1.16.1";
8098
8120
 
8099
8121
  // src/tools/tasks.ts
8100
8122
  import { z as z2 } from "zod";
@@ -10468,16 +10490,18 @@ var GetVideoScriptInput = z5.object({
10468
10490
  taskId: z5.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
10469
10491
  angleKey: z5.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.')
10470
10492
  }).strict();
10493
+ var VIDEO_SCRIPT_APPROVAL_MAX_SCENES = 50;
10494
+ var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS = 2e3;
10471
10495
  var ApproveSceneEdit = z5.object({
10472
10496
  index: z5.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
10473
- dialogue: z5.string().min(1).refine((v) => v.trim().length > 0, {
10497
+ dialogue: z5.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS).refine((v) => v.trim().length > 0, {
10474
10498
  message: "dialogue no puede ser s\xF3lo espacios"
10475
10499
  }).describe("Di\xE1logo final de la escena. NO puede estar vac\xEDo ni ser s\xF3lo espacios. Puede ser el original sin cambios o la edici\xF3n del usuario.")
10476
10500
  }).strict();
10477
10501
  var ApproveVideoScriptInput = z5.object({
10478
10502
  taskId: z5.string().min(1).describe("ID de la tarea."),
10479
- angleKey: z5.string().min(1).optional().describe('Mismo angleKey usado en get_video_script. REQUERIDO en multi-angle (si se omite el backend resuelve a "angle_0" literal). En single-angle puede omitirse.'),
10480
- scenes: z5.array(ApproveSceneEdit).min(1).describe("Escenas con los di\xE1logos finales. El backend mergea por `index` \u2014 las escenas NO incluidas conservan el di\xE1logo original. Debe haber al menos 1 entrada (scenes vac\xEDo = 400). Para cada entrada que mandes con un dialogue distinto del original, el backend valida minWords \u2264 words \u2264 maxWords y rechaza con 400 si te sales del rango.")
10503
+ angleKey: z5.string().trim().min(1).max(120).optional().describe('Mismo angleKey usado en get_video_script. REQUERIDO en multi-angle (si se omite el backend resuelve a "angle_0" literal). En single-angle puede omitirse.'),
10504
+ scenes: z5.array(ApproveSceneEdit).min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_SCENES).describe("Escenas con los di\xE1logos finales. El backend mergea por `index` \u2014 las escenas NO incluidas conservan el di\xE1logo original. Debe haber entre 1 y 50 entradas. Para cada entrada que mandes con un dialogue distinto del original, el backend valida minWords \u2264 words \u2264 maxWords y rechaza con 400 si te sales del rango.")
10481
10505
  }).strict();
10482
10506
  var RegenerateVideoScriptInput = z5.object({
10483
10507
  taskId: z5.string().min(1).describe("ID de la tarea."),
@@ -10485,7 +10509,7 @@ var RegenerateVideoScriptInput = z5.object({
10485
10509
  narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion. Incluye valores hist\xF3ricos porque esta herramienta tambi\xE9n regenera tareas antiguas. REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).')
10486
10510
  }).strict();
10487
10511
  function registerScriptReviewTools(server, api) {
10488
- server.tool("get_video_script", 'Lee el guion del video pendiente de revisi\xF3n. S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId? }], error? }. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado (p.ej. el guion no cab\xEDa en el tiempo y el \xE1ngulo volvi\xF3 a "ready"); mu\xE9straselo al usuario.\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y a partir de ah\xED llama este tool peri\xF3dicamente \u2014 la respuesta de get_video_script trae su propio campo `status` que es la se\xF1al real:\n- "ready" \u2192 el guion est\xE1 listo; mu\xE9strale al usuario cada `scenes[].dialogue` por `index` con su tope `maxWords`, recoge ediciones, llama approve_video_script.\n- "not_ready" \u2192 el guion a\xFAn se est\xE1 generando; espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle puede ser que ese \xE1ngulo ya est\xE9 hecho \u2014 vuelve a llamar con otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar en una pausa previa.\n- "error" \u2192 el backend no pudo generar el guion; usa `regenerate_video_script` para re-intentar (es la ruta de recuperaci\xF3n v\xE1lida \u2014 el guion no se aprueba en estado "error").\n\nMULTI-ANGLE: si la tarea tiene varios \xE1ngulos, omitir angleKey siempre devuelve "angle_0" literal \u2014 itera expl\xEDcitamente por "angle_0", "angle_1", ... hasta encontrar uno con status="ready".', GetVideoScriptInput.shape, async (input) => {
10512
+ server.tool("get_video_script", 'Lee el guion del video pendiente de revisi\xF3n. S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId?, persuasiveFunction?, winnerVisual?, expectedVisual? }], error? }. En escenas copiadas de un winner, `persuasiveFunction` explica qu\xE9 logra ese beat, `winnerVisual` conserva la observaci\xF3n fuente y `expectedVisual` describe nuestra adaptaci\xF3n localizada; muestra los tres junto al di\xE1logo para que una edici\xF3n no desaline\xE9 guion y visual. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado (p.ej. el guion no cab\xEDa en el tiempo y el \xE1ngulo volvi\xF3 a "ready"); mu\xE9straselo al usuario.\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y a partir de ah\xED llama este tool peri\xF3dicamente \u2014 la respuesta de get_video_script trae su propio campo `status` que es la se\xF1al real:\n- "ready" \u2192 el guion est\xE1 listo; mu\xE9strale al usuario cada `scenes[].dialogue` por `index` con su tope `maxWords`, recoge ediciones, llama approve_video_script.\n- "not_ready" \u2192 el guion a\xFAn se est\xE1 generando; espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle puede ser que ese \xE1ngulo ya est\xE9 hecho \u2014 vuelve a llamar con otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar en una pausa previa.\n- "error" \u2192 el backend no pudo generar el guion; usa `regenerate_video_script` para re-intentar (es la ruta de recuperaci\xF3n v\xE1lida \u2014 el guion no se aprueba en estado "error").\n\nMULTI-ANGLE: si la tarea tiene varios \xE1ngulos, omitir angleKey siempre devuelve "angle_0" literal \u2014 itera expl\xEDcitamente por "angle_0", "angle_1", ... hasta encontrar uno con status="ready".', GetVideoScriptInput.shape, async (input) => {
10489
10513
  try {
10490
10514
  const qs = input.angleKey ? `?angleKey=${encodeURIComponent(input.angleKey)}` : "";
10491
10515
  const r = await api.getJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/video-script${qs}`);
package/dist/stdio.js CHANGED
@@ -2349,7 +2349,7 @@ var require_video = __commonJS({
2349
2349
  Object.defineProperty(exports, "__esModule", {
2350
2350
  value: true
2351
2351
  });
2352
- exports.RegenerateVideoResponse = exports.RegenerateVideoRequest = exports.RegenerateVideoScriptNarrativeResponse = exports.RegenerateVideoScriptNarrativeRequest = exports.ApproveVideoScriptResponse = exports.ApproveVideoScriptRequest = exports.GetVideoScriptForReviewResponse = exports.VideoScriptSceneForReview = exports.VideoGenerator = exports.VideoGeneratorVariant = exports.ScriptReviewStatus = exports.VideoGeneratorStatus = void 0;
2352
+ exports.RegenerateVideoResponse = exports.RegenerateVideoRequest = exports.RegenerateVideoScriptNarrativeResponse = exports.RegenerateVideoScriptNarrativeRequest = exports.ApproveVideoScriptResponse = exports.ApproveVideoScriptRequest = exports.VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS = exports.VIDEO_SCRIPT_APPROVAL_MAX_SCENES = exports.GetVideoScriptForReviewResponse = exports.VideoScriptSceneForReview = exports.VideoGenerator = exports.VideoGeneratorVariant = exports.ScriptReviewStatus = exports.VideoGeneratorStatus = void 0;
2353
2353
  var zod_1 = __require("zod");
2354
2354
  var video_enums_1 = require_video_enums();
2355
2355
  exports.VideoGeneratorStatus = zod_1.z.enum([
@@ -2424,7 +2424,14 @@ var require_video = __commonJS({
2424
2424
  * mute shot. The review UI must NOT block approval on an empty scene when this
2425
2425
  * is set. Absent/false → a spoken beat that must carry dialogue (the approval
2426
2426
  * gate blocks an empty one). Optional for backwards compatibility. */
2427
- silentAllowed: zod_1.z.boolean().optional()
2427
+ silentAllowed: zod_1.z.boolean().optional(),
2428
+ /** Winner scene's persuasive job. Shown beside editable dialogue so edits
2429
+ * preserve why the source beat exists. */
2430
+ persuasiveFunction: zod_1.z.string().optional(),
2431
+ /** Objective visual observed in the winner source scene. */
2432
+ winnerVisual: zod_1.z.string().optional(),
2433
+ /** Localized target visual expected to accompany this spoken line. */
2434
+ expectedVisual: zod_1.z.string().optional()
2428
2435
  });
2429
2436
  exports.GetVideoScriptForReviewResponse = zod_1.z.object({
2430
2437
  status: exports.ScriptReviewStatus,
@@ -2437,13 +2444,15 @@ var require_video = __commonJS({
2437
2444
  * reverts an approved card back to "Revisar" without telling the user why. */
2438
2445
  error: zod_1.z.string().optional()
2439
2446
  });
2447
+ exports.VIDEO_SCRIPT_APPROVAL_MAX_SCENES = 50;
2448
+ exports.VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS = 2e3;
2440
2449
  exports.ApproveVideoScriptRequest = zod_1.z.object({
2441
- angleKey: zod_1.z.string().min(1).optional(),
2450
+ angleKey: zod_1.z.string().trim().min(1).max(120).optional(),
2442
2451
  scenes: zod_1.z.array(zod_1.z.object({
2443
2452
  index: zod_1.z.number().int().nonnegative(),
2444
- dialogue: zod_1.z.string()
2445
- })).min(1)
2446
- });
2453
+ dialogue: zod_1.z.string().trim().min(1).max(exports.VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS)
2454
+ }).strict()).min(1).max(exports.VIDEO_SCRIPT_APPROVAL_MAX_SCENES)
2455
+ }).strict();
2447
2456
  exports.ApproveVideoScriptResponse = zod_1.z.object({
2448
2457
  status: exports.ScriptReviewStatus
2449
2458
  });
@@ -6024,15 +6033,16 @@ var require_pricing = __commonJS({
6024
6033
  // Observed on the gap render 090ce235-55bd-401c-8ae8-ce362f4d7769 (local, the
6025
6034
  // only place these ran — zero prod BillingEntry rows for these labels as of
6026
6035
  // 2026-07-18, so there is no prod p75 to calibrate against yet):
6027
- // gap-panel-fidelity-qa ×5 = 0.700¢ our → 0.14¢ our / 0.28¢ user each
6036
+ // Grok 4.5 HIGH is billed on actual token use. A recent P2 E2E measured
6037
+ // ~0.74¢/panel, so quote a conservative whole-cent ceiling per call.
6028
6038
  // clip-payoff-judge ×2 = 2.638¢ our → 1.32¢ our / 2.64¢ user each
6029
6039
  // ⚠️ OWNER DECISION PENDING: these are provisional ceilings from ONE render.
6030
6040
  // Re-derive from prod BillingEntry once gap_replication has volume.
6031
6041
  // Unit = one per-panel gap fidelity judge (3 images: our panel + the winner
6032
6042
  // keyframe + the product reference). Always on for gap_replication.
6033
6043
  "openrouter:gap-panel-fidelity-qa": {
6034
- ourCostCents: 0.2,
6035
- userPriceCents: 0.4
6044
+ ourCostCents: 1,
6045
+ userPriceCents: 2
6036
6046
  },
6037
6047
  // Unit = one payoff-judge pass over a RENDERED clip (video input → the most
6038
6048
  // expensive of the three). Behind PAYOFF_JUDGE_ENABLED (default OFF).
@@ -6142,6 +6152,18 @@ var require_pricing = __commonJS({
6142
6152
  ourCostCents: 1,
6143
6153
  userPriceCents: 2
6144
6154
  },
6155
+ // P2 post-edit path. These two high-reasoning text calls run only when a user
6156
+ // changes a winner-mapped spoken line: first localized reconciliation, then
6157
+ // an independent whole-plan audit. Both are unmeasured at launch; quote each
6158
+ // at the same conservative ceiling as gap-shot-alignment and bill actual use.
6159
+ "openrouter:gap-script-reconciliation": {
6160
+ ourCostCents: 1,
6161
+ userPriceCents: 2
6162
+ },
6163
+ "openrouter:gap-script-visual-audit": {
6164
+ ourCostCents: 1,
6165
+ userPriceCents: 2
6166
+ },
6145
6167
  // Unit = one ffmpeg mix laying the replicated winner's own audio track under our
6146
6168
  // voiceover, producing the extra `ad_final_with_winner_music` deliverable
6147
6169
  // (gap_replication only, behind GAP_WINNER_MUSIC_ENABLED, default OFF). No AI
@@ -8091,7 +8113,7 @@ var ApiClient = class {
8091
8113
  };
8092
8114
 
8093
8115
  // src/index.ts
8094
- var MCP_VERSION = "1.16.0";
8116
+ var MCP_VERSION = "1.16.1";
8095
8117
 
8096
8118
  // src/tools/tasks.ts
8097
8119
  import { z as z2 } from "zod";
@@ -10424,16 +10446,18 @@ var GetVideoScriptInput = z5.object({
10424
10446
  taskId: z5.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
10425
10447
  angleKey: z5.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.')
10426
10448
  }).strict();
10449
+ var VIDEO_SCRIPT_APPROVAL_MAX_SCENES = 50;
10450
+ var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS = 2e3;
10427
10451
  var ApproveSceneEdit = z5.object({
10428
10452
  index: z5.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
10429
- dialogue: z5.string().min(1).refine((v) => v.trim().length > 0, {
10453
+ dialogue: z5.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS).refine((v) => v.trim().length > 0, {
10430
10454
  message: "dialogue no puede ser s\xF3lo espacios"
10431
10455
  }).describe("Di\xE1logo final de la escena. NO puede estar vac\xEDo ni ser s\xF3lo espacios. Puede ser el original sin cambios o la edici\xF3n del usuario.")
10432
10456
  }).strict();
10433
10457
  var ApproveVideoScriptInput = z5.object({
10434
10458
  taskId: z5.string().min(1).describe("ID de la tarea."),
10435
- angleKey: z5.string().min(1).optional().describe('Mismo angleKey usado en get_video_script. REQUERIDO en multi-angle (si se omite el backend resuelve a "angle_0" literal). En single-angle puede omitirse.'),
10436
- scenes: z5.array(ApproveSceneEdit).min(1).describe("Escenas con los di\xE1logos finales. El backend mergea por `index` \u2014 las escenas NO incluidas conservan el di\xE1logo original. Debe haber al menos 1 entrada (scenes vac\xEDo = 400). Para cada entrada que mandes con un dialogue distinto del original, el backend valida minWords \u2264 words \u2264 maxWords y rechaza con 400 si te sales del rango.")
10459
+ angleKey: z5.string().trim().min(1).max(120).optional().describe('Mismo angleKey usado en get_video_script. REQUERIDO en multi-angle (si se omite el backend resuelve a "angle_0" literal). En single-angle puede omitirse.'),
10460
+ scenes: z5.array(ApproveSceneEdit).min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_SCENES).describe("Escenas con los di\xE1logos finales. El backend mergea por `index` \u2014 las escenas NO incluidas conservan el di\xE1logo original. Debe haber entre 1 y 50 entradas. Para cada entrada que mandes con un dialogue distinto del original, el backend valida minWords \u2264 words \u2264 maxWords y rechaza con 400 si te sales del rango.")
10437
10461
  }).strict();
10438
10462
  var RegenerateVideoScriptInput = z5.object({
10439
10463
  taskId: z5.string().min(1).describe("ID de la tarea."),
@@ -10441,7 +10465,7 @@ var RegenerateVideoScriptInput = z5.object({
10441
10465
  narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion. Incluye valores hist\xF3ricos porque esta herramienta tambi\xE9n regenera tareas antiguas. REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).')
10442
10466
  }).strict();
10443
10467
  function registerScriptReviewTools(server, api) {
10444
- server.tool("get_video_script", 'Lee el guion del video pendiente de revisi\xF3n. S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId? }], error? }. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado (p.ej. el guion no cab\xEDa en el tiempo y el \xE1ngulo volvi\xF3 a "ready"); mu\xE9straselo al usuario.\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y a partir de ah\xED llama este tool peri\xF3dicamente \u2014 la respuesta de get_video_script trae su propio campo `status` que es la se\xF1al real:\n- "ready" \u2192 el guion est\xE1 listo; mu\xE9strale al usuario cada `scenes[].dialogue` por `index` con su tope `maxWords`, recoge ediciones, llama approve_video_script.\n- "not_ready" \u2192 el guion a\xFAn se est\xE1 generando; espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle puede ser que ese \xE1ngulo ya est\xE9 hecho \u2014 vuelve a llamar con otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar en una pausa previa.\n- "error" \u2192 el backend no pudo generar el guion; usa `regenerate_video_script` para re-intentar (es la ruta de recuperaci\xF3n v\xE1lida \u2014 el guion no se aprueba en estado "error").\n\nMULTI-ANGLE: si la tarea tiene varios \xE1ngulos, omitir angleKey siempre devuelve "angle_0" literal \u2014 itera expl\xEDcitamente por "angle_0", "angle_1", ... hasta encontrar uno con status="ready".', GetVideoScriptInput.shape, async (input) => {
10468
+ server.tool("get_video_script", 'Lee el guion del video pendiente de revisi\xF3n. S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId?, persuasiveFunction?, winnerVisual?, expectedVisual? }], error? }. En escenas copiadas de un winner, `persuasiveFunction` explica qu\xE9 logra ese beat, `winnerVisual` conserva la observaci\xF3n fuente y `expectedVisual` describe nuestra adaptaci\xF3n localizada; muestra los tres junto al di\xE1logo para que una edici\xF3n no desaline\xE9 guion y visual. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado (p.ej. el guion no cab\xEDa en el tiempo y el \xE1ngulo volvi\xF3 a "ready"); mu\xE9straselo al usuario.\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y a partir de ah\xED llama este tool peri\xF3dicamente \u2014 la respuesta de get_video_script trae su propio campo `status` que es la se\xF1al real:\n- "ready" \u2192 el guion est\xE1 listo; mu\xE9strale al usuario cada `scenes[].dialogue` por `index` con su tope `maxWords`, recoge ediciones, llama approve_video_script.\n- "not_ready" \u2192 el guion a\xFAn se est\xE1 generando; espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle puede ser que ese \xE1ngulo ya est\xE9 hecho \u2014 vuelve a llamar con otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar en una pausa previa.\n- "error" \u2192 el backend no pudo generar el guion; usa `regenerate_video_script` para re-intentar (es la ruta de recuperaci\xF3n v\xE1lida \u2014 el guion no se aprueba en estado "error").\n\nMULTI-ANGLE: si la tarea tiene varios \xE1ngulos, omitir angleKey siempre devuelve "angle_0" literal \u2014 itera expl\xEDcitamente por "angle_0", "angle_1", ... hasta encontrar uno con status="ready".', GetVideoScriptInput.shape, async (input) => {
10445
10469
  try {
10446
10470
  const qs = input.angleKey ? `?angleKey=${encodeURIComponent(input.angleKey)}` : "";
10447
10471
  const r = await api.getJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/video-script${qs}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "1.16.0",
3
+ "version": "1.16.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Product Maker MCP server — cost products, generate dropshipping landing pages, UGC videos, and ad creatives, and publish to Shopify and Meta Ads from Claude, ChatGPT, or Gemini.",
@@ -63,8 +63,8 @@
63
63
  "tsx": "^4.16.0",
64
64
  "typescript": "^7.0.2",
65
65
  "vitest": "^2.1.9",
66
- "@packages/product-economics": "0.1.0",
67
- "@packages/types": "0.1.0"
66
+ "@packages/types": "0.1.0",
67
+ "@packages/product-economics": "0.1.0"
68
68
  },
69
69
  "publishConfig": {
70
70
  "access": "public"