@productmaker/mcp 11.8.0 → 11.8.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.
- package/README.md +21 -1
- package/dist/http.js +1016 -935
- package/dist/stdio.js +1016 -935
- package/package.json +1 -1
package/dist/stdio.js
CHANGED
|
@@ -7262,6 +7262,8 @@ var ApproveVideoAnglesRequest = z35.object({
|
|
|
7262
7262
|
var ApproveVideoAnglesResponse = z35.object({
|
|
7263
7263
|
success: z35.boolean(),
|
|
7264
7264
|
taskId: z35.string().uuid(),
|
|
7265
|
+
/** Persisted keys scheduled by this approval; visible candidate indices are not worker keys. */
|
|
7266
|
+
approvedAngleKeys: z35.array(z35.string().regex(/^angle_\d+$/)).min(1).max(3).optional(),
|
|
7265
7267
|
reason: ApproveAnglesRefusalReason.optional()
|
|
7266
7268
|
});
|
|
7267
7269
|
var SaveVideoDraftRequest = z35.object({
|
|
@@ -19509,7 +19511,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
19509
19511
|
], [
|
|
19510
19512
|
"campaign_status"
|
|
19511
19513
|
]),
|
|
19512
|
-
defineCapability("get_video_script", "
|
|
19514
|
+
defineCapability("get_video_script", "e987ff48555dfdab439f9910fb1fa25af373c78c8a9a5ef26458c82add90f2a2", "none", "read_only", [
|
|
19513
19515
|
"read"
|
|
19514
19516
|
], [
|
|
19515
19517
|
slot("task", true, [
|
|
@@ -19905,7 +19907,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
19905
19907
|
], [
|
|
19906
19908
|
"brand"
|
|
19907
19909
|
], MCP_ONLY_CHANNEL_EXPOSURE),
|
|
19908
|
-
defineCapability("create_service_landing", "
|
|
19910
|
+
defineCapability("create_service_landing", "ce99d75f4b00bea23b6bf4f56ef3331b266176cb0a9dead55611ee78d21c41b3", "credit_consuming", "creative", [
|
|
19909
19911
|
"credit_consuming",
|
|
19910
19912
|
"state_change"
|
|
19911
19913
|
], [
|
|
@@ -22323,51 +22325,6 @@ var ServicePageGenerateRequestSchema = z104.object({
|
|
|
22323
22325
|
instruction: ServicePageEditInstructionSchema.optional(),
|
|
22324
22326
|
attachmentRefs: ServicePageAttachmentRefsSchema.optional()
|
|
22325
22327
|
}).strict();
|
|
22326
|
-
var SERVICE_PAGE_SOURCES_MISSING_CODES = /* @__PURE__ */ new Set([
|
|
22327
|
-
"SERVICE_PAGE_SOURCES_REQUIRED",
|
|
22328
|
-
"SERVICE_PAGE_SOURCE_TEXT_REQUIRED"
|
|
22329
|
-
]);
|
|
22330
|
-
function isRetryableServicePageFailureCode(errorCode) {
|
|
22331
|
-
return servicePageFailurePolicy(errorCode).retryable;
|
|
22332
|
-
}
|
|
22333
|
-
__name(isRetryableServicePageFailureCode, "isRetryableServicePageFailureCode");
|
|
22334
|
-
function servicePageFailurePolicy(errorCode) {
|
|
22335
|
-
const code = errorCode ?? "";
|
|
22336
|
-
const retryable = !SERVICE_PAGE_SOURCES_MISSING_CODES.has(code) && !/CREDIT|QUOTE|REVISION|CONFLICT|WINDOW_EXPIRED|NOT_FOUND|POLICY_INVALID|SNAPSHOT_INVALID|OWNER_MISMATCH|PROVIDER_ROUTE_UNSUPPORTED|PROVIDER_REJECTED/.test(code);
|
|
22337
|
-
if (code.includes("QUOTE_EXPIRED")) return {
|
|
22338
|
-
reason: "quote_expired",
|
|
22339
|
-
retryable
|
|
22340
|
-
};
|
|
22341
|
-
if (code.includes("CREDIT")) return {
|
|
22342
|
-
reason: "credits",
|
|
22343
|
-
retryable
|
|
22344
|
-
};
|
|
22345
|
-
if (code.includes("NOT_FOUND")) return {
|
|
22346
|
-
reason: "not_found",
|
|
22347
|
-
retryable
|
|
22348
|
-
};
|
|
22349
|
-
if (/PROVIDER|ACCEPTANCE/.test(code)) return {
|
|
22350
|
-
reason: "provider",
|
|
22351
|
-
retryable
|
|
22352
|
-
};
|
|
22353
|
-
if (/NETWORK|TIMEOUT|QUEUE/.test(code)) return {
|
|
22354
|
-
reason: "network",
|
|
22355
|
-
retryable
|
|
22356
|
-
};
|
|
22357
|
-
if (/REVISION|STALE|CONFLICT/.test(code)) return {
|
|
22358
|
-
reason: "conflict",
|
|
22359
|
-
retryable
|
|
22360
|
-
};
|
|
22361
|
-
if (/INVALID|PRECONDITION|MISSING|REQUIRED|MISMATCH/.test(code)) return {
|
|
22362
|
-
reason: "validation",
|
|
22363
|
-
retryable
|
|
22364
|
-
};
|
|
22365
|
-
return {
|
|
22366
|
-
reason: "unknown",
|
|
22367
|
-
retryable
|
|
22368
|
-
};
|
|
22369
|
-
}
|
|
22370
|
-
__name(servicePageFailurePolicy, "servicePageFailurePolicy");
|
|
22371
22328
|
var SERVICE_PAGE_CREATION_MAX_ATTEMPTS = 3;
|
|
22372
22329
|
var ServicePageCreationMarkerSchema = z104.object({
|
|
22373
22330
|
rootOperationId: z104.string().uuid().optional(),
|
|
@@ -23867,7 +23824,7 @@ var ApiClient = class {
|
|
|
23867
23824
|
};
|
|
23868
23825
|
|
|
23869
23826
|
// src/index.ts
|
|
23870
|
-
var MCP_VERSION = "11.8.
|
|
23827
|
+
var MCP_VERSION = "11.8.1";
|
|
23871
23828
|
|
|
23872
23829
|
// src/meta-upload-app.ts
|
|
23873
23830
|
import { readFileSync } from "fs";
|
|
@@ -23939,7 +23896,7 @@ function registerMediaUploadAppResource(server, resolveEndpoint) {
|
|
|
23939
23896
|
__name(registerMediaUploadAppResource, "registerMediaUploadAppResource");
|
|
23940
23897
|
|
|
23941
23898
|
// src/tools/tasks.ts
|
|
23942
|
-
import { z as
|
|
23899
|
+
import { z as z121 } from "zod";
|
|
23943
23900
|
|
|
23944
23901
|
// src/resolve-image.ts
|
|
23945
23902
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -25553,6 +25510,9 @@ function approveRefusalMessage(reason) {
|
|
|
25553
25510
|
}
|
|
25554
25511
|
__name(approveRefusalMessage, "approveRefusalMessage");
|
|
25555
25512
|
|
|
25513
|
+
// src/tools/task-outputs.ts
|
|
25514
|
+
import { z as z117 } from "zod";
|
|
25515
|
+
|
|
25556
25516
|
// src/asset-proxy.ts
|
|
25557
25517
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
25558
25518
|
var TOKEN_TTL_SECONDS = 3600;
|
|
@@ -25606,8 +25566,64 @@ function buildProxyUrl(opts) {
|
|
|
25606
25566
|
}
|
|
25607
25567
|
__name(buildProxyUrl, "buildProxyUrl");
|
|
25608
25568
|
|
|
25569
|
+
// src/tools/conversational-review-format.ts
|
|
25570
|
+
function escapeMarkdownText(value) {
|
|
25571
|
+
return value.replace(/\\/g, "\\\\").replace(/([`*_{}[\]()<>#+.!|>])/g, "\\$1").replace(/[\r\n\u2028\u2029]+/g, " ");
|
|
25572
|
+
}
|
|
25573
|
+
__name(escapeMarkdownText, "escapeMarkdownText");
|
|
25574
|
+
function trustedPreviewUrl(raw) {
|
|
25575
|
+
if (/[\u0000-\u001f\u007f]/.test(raw)) return null;
|
|
25576
|
+
try {
|
|
25577
|
+
const url = new URL(raw);
|
|
25578
|
+
if (url.username || url.password) return null;
|
|
25579
|
+
const hostname = url.hostname.toLowerCase();
|
|
25580
|
+
const productMakerHost = hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
|
|
25581
|
+
const localHost = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
25582
|
+
if (url.protocol === "https:" && productMakerHost && (!url.port || url.port === "443")) {
|
|
25583
|
+
return url.toString().replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
25584
|
+
}
|
|
25585
|
+
if (url.protocol === "http:" && localHost) {
|
|
25586
|
+
return url.toString().replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
25587
|
+
}
|
|
25588
|
+
return null;
|
|
25589
|
+
} catch {
|
|
25590
|
+
return null;
|
|
25591
|
+
}
|
|
25592
|
+
}
|
|
25593
|
+
__name(trustedPreviewUrl, "trustedPreviewUrl");
|
|
25594
|
+
function renderPreview(preview2, index) {
|
|
25595
|
+
const label = escapeMarkdownText(preview2.label);
|
|
25596
|
+
const url = trustedPreviewUrl(preview2.previewUrl);
|
|
25597
|
+
return [
|
|
25598
|
+
`### Foto ${index + 1}: ${label}`,
|
|
25599
|
+
...preview2.view ? [
|
|
25600
|
+
`- **Vista:** ${escapeMarkdownText(preview2.view)}`
|
|
25601
|
+
] : [],
|
|
25602
|
+
...url ? [
|
|
25603
|
+
``
|
|
25604
|
+
] : [
|
|
25605
|
+
"- Vista previa externa bloqueada por seguridad. \xC1brela desde ProductMaker Web."
|
|
25606
|
+
]
|
|
25607
|
+
];
|
|
25608
|
+
}
|
|
25609
|
+
__name(renderPreview, "renderPreview");
|
|
25610
|
+
function formatCredits(creditCents) {
|
|
25611
|
+
return new Intl.NumberFormat("es-CO", {
|
|
25612
|
+
minimumFractionDigits: creditCents % 100 === 0 ? 0 : 2,
|
|
25613
|
+
maximumFractionDigits: 2
|
|
25614
|
+
}).format(creditCents / 100);
|
|
25615
|
+
}
|
|
25616
|
+
__name(formatCredits, "formatCredits");
|
|
25617
|
+
|
|
25609
25618
|
// src/tools/task-outputs.ts
|
|
25610
25619
|
var WEBAPP_BASE = "https://productmaker.app";
|
|
25620
|
+
var CreativeVariantIdentity = z117.object({
|
|
25621
|
+
variantTaskId: z117.string().uuid(),
|
|
25622
|
+
sourceTaskId: z117.string().uuid(),
|
|
25623
|
+
kind: TaskCreativeVariantKind,
|
|
25624
|
+
status: TaskStatusSchema,
|
|
25625
|
+
error: z117.string().optional()
|
|
25626
|
+
});
|
|
25611
25627
|
function proxifyOutputs(outputs, proxyBaseUrl) {
|
|
25612
25628
|
if (!proxyBaseUrl || !isProxyEnabled()) return outputs;
|
|
25613
25629
|
const rewrite = /* @__PURE__ */ __name((url, filename) => buildProxyUrl({
|
|
@@ -25651,7 +25667,15 @@ function proxifyOutputs(outputs, proxyBaseUrl) {
|
|
|
25651
25667
|
...variant,
|
|
25652
25668
|
url: rewrite(variant.url, `video-${sanitizeFilename(v.angleKey)}-${sanitizeFilename(variant.kind)}.mp4`)
|
|
25653
25669
|
}))
|
|
25654
|
-
}))
|
|
25670
|
+
})),
|
|
25671
|
+
...outputs.creativeVariants?.length ? {
|
|
25672
|
+
creativeVariants: outputs.creativeVariants.map((child) => ({
|
|
25673
|
+
...child,
|
|
25674
|
+
...child.outputs ? {
|
|
25675
|
+
outputs: proxifyOutputs(child.outputs, proxyBaseUrl)
|
|
25676
|
+
} : {}
|
|
25677
|
+
}))
|
|
25678
|
+
} : {}
|
|
25655
25679
|
};
|
|
25656
25680
|
}
|
|
25657
25681
|
__name(proxifyOutputs, "proxifyOutputs");
|
|
@@ -25661,6 +25685,37 @@ function sanitizeFilename(s) {
|
|
|
25661
25685
|
__name(sanitizeFilename, "sanitizeFilename");
|
|
25662
25686
|
async function fetchTaskOutputs(api, taskId, options) {
|
|
25663
25687
|
const raw = await api.getJson(`/v1/results/${encodeURIComponent(taskId)}?signedUrls=true`, options);
|
|
25688
|
+
const creativeVariants = (raw.creativeVariants ?? []).flatMap((child) => {
|
|
25689
|
+
const identity = CreativeVariantIdentity.safeParse(child);
|
|
25690
|
+
if (!identity.success || identity.data.sourceTaskId !== taskId) return [];
|
|
25691
|
+
const { error, ...details } = identity.data;
|
|
25692
|
+
return [
|
|
25693
|
+
{
|
|
25694
|
+
...details,
|
|
25695
|
+
...error ? {
|
|
25696
|
+
error: error.slice(0, 1e3)
|
|
25697
|
+
} : {},
|
|
25698
|
+
nextAction: {
|
|
25699
|
+
tool: "get_task_status",
|
|
25700
|
+
arguments: {
|
|
25701
|
+
taskId: details.variantTaskId
|
|
25702
|
+
}
|
|
25703
|
+
},
|
|
25704
|
+
...details.status === "done" ? {
|
|
25705
|
+
outputs: projectTaskOutputs(child, details.variantTaskId)
|
|
25706
|
+
} : {}
|
|
25707
|
+
}
|
|
25708
|
+
];
|
|
25709
|
+
});
|
|
25710
|
+
return {
|
|
25711
|
+
...projectTaskOutputs(raw, taskId),
|
|
25712
|
+
...creativeVariants.length ? {
|
|
25713
|
+
creativeVariants
|
|
25714
|
+
} : {}
|
|
25715
|
+
};
|
|
25716
|
+
}
|
|
25717
|
+
__name(fetchTaskOutputs, "fetchTaskOutputs");
|
|
25718
|
+
function projectTaskOutputs(raw, taskId) {
|
|
25664
25719
|
const videos = (raw.videoGenerators ?? []).map((vg) => {
|
|
25665
25720
|
const variants2 = (vg.variants ?? []).filter((v) => Boolean(v.kind) && Boolean(v.url)).map((v) => ({
|
|
25666
25721
|
kind: v.kind,
|
|
@@ -25745,7 +25800,7 @@ async function fetchTaskOutputs(api, taskId, options) {
|
|
|
25745
25800
|
adCopy: typeof raw.adCreative?.adCopy === "string" && raw.adCreative.adCopy.length ? raw.adCreative.adCopy : void 0
|
|
25746
25801
|
};
|
|
25747
25802
|
}
|
|
25748
|
-
__name(
|
|
25803
|
+
__name(projectTaskOutputs, "projectTaskOutputs");
|
|
25749
25804
|
function renderOutputsMarkdown(outputs) {
|
|
25750
25805
|
const parts = [];
|
|
25751
25806
|
parts.push(`\u2705 **Tu tarea est\xE1 lista** \u2014 [Abrir en Product Maker](${outputs.webappUrl})`);
|
|
@@ -25787,6 +25842,18 @@ function renderOutputsMarkdown(outputs) {
|
|
|
25787
25842
|
}
|
|
25788
25843
|
}
|
|
25789
25844
|
}
|
|
25845
|
+
for (const child of outputs.creativeVariants ?? []) {
|
|
25846
|
+
const label = child.kind === "image" ? "imagen" : child.kind;
|
|
25847
|
+
parts.push(`
|
|
25848
|
+
### Variante de ${label}`);
|
|
25849
|
+
if (child.status === "done" && child.outputs) {
|
|
25850
|
+
parts.push(renderOutputsMarkdown(child.outputs));
|
|
25851
|
+
} else if (child.status === "error") {
|
|
25852
|
+
parts.push(escapeMarkdownText(child.error ?? "No se pudo entregar esta variante."));
|
|
25853
|
+
} else {
|
|
25854
|
+
parts.push("La variante sigue gener\xE1ndose. Consulta su estado antes de iniciar otra.");
|
|
25855
|
+
}
|
|
25856
|
+
}
|
|
25790
25857
|
return parts.join("\n");
|
|
25791
25858
|
}
|
|
25792
25859
|
__name(renderOutputsMarkdown, "renderOutputsMarkdown");
|
|
@@ -25830,55 +25897,6 @@ __name(outputGifs, "outputGifs");
|
|
|
25830
25897
|
var PendingPresentationSchema = PendingUserActionSchema;
|
|
25831
25898
|
var VideoScriptSchema = GetVideoScriptForReviewResponse;
|
|
25832
25899
|
|
|
25833
|
-
// src/tools/conversational-review-format.ts
|
|
25834
|
-
function escapeMarkdownText(value) {
|
|
25835
|
-
return value.replace(/\\/g, "\\\\").replace(/([`*_{}[\]()<>#+.!|>])/g, "\\$1").replace(/[\r\n\u2028\u2029]+/g, " ");
|
|
25836
|
-
}
|
|
25837
|
-
__name(escapeMarkdownText, "escapeMarkdownText");
|
|
25838
|
-
function trustedPreviewUrl(raw) {
|
|
25839
|
-
if (/[\u0000-\u001f\u007f]/.test(raw)) return null;
|
|
25840
|
-
try {
|
|
25841
|
-
const url = new URL(raw);
|
|
25842
|
-
if (url.username || url.password) return null;
|
|
25843
|
-
const hostname = url.hostname.toLowerCase();
|
|
25844
|
-
const productMakerHost = hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
|
|
25845
|
-
const localHost = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
25846
|
-
if (url.protocol === "https:" && productMakerHost && (!url.port || url.port === "443")) {
|
|
25847
|
-
return url.toString().replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
25848
|
-
}
|
|
25849
|
-
if (url.protocol === "http:" && localHost) {
|
|
25850
|
-
return url.toString().replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
25851
|
-
}
|
|
25852
|
-
return null;
|
|
25853
|
-
} catch {
|
|
25854
|
-
return null;
|
|
25855
|
-
}
|
|
25856
|
-
}
|
|
25857
|
-
__name(trustedPreviewUrl, "trustedPreviewUrl");
|
|
25858
|
-
function renderPreview(preview2, index) {
|
|
25859
|
-
const label = escapeMarkdownText(preview2.label);
|
|
25860
|
-
const url = trustedPreviewUrl(preview2.previewUrl);
|
|
25861
|
-
return [
|
|
25862
|
-
`### Foto ${index + 1}: ${label}`,
|
|
25863
|
-
...preview2.view ? [
|
|
25864
|
-
`- **Vista:** ${escapeMarkdownText(preview2.view)}`
|
|
25865
|
-
] : [],
|
|
25866
|
-
...url ? [
|
|
25867
|
-
``
|
|
25868
|
-
] : [
|
|
25869
|
-
"- Vista previa externa bloqueada por seguridad. \xC1brela desde ProductMaker Web."
|
|
25870
|
-
]
|
|
25871
|
-
];
|
|
25872
|
-
}
|
|
25873
|
-
__name(renderPreview, "renderPreview");
|
|
25874
|
-
function formatCredits(creditCents) {
|
|
25875
|
-
return new Intl.NumberFormat("es-CO", {
|
|
25876
|
-
minimumFractionDigits: creditCents % 100 === 0 ? 0 : 2,
|
|
25877
|
-
maximumFractionDigits: 2
|
|
25878
|
-
}).format(creditCents / 100);
|
|
25879
|
-
}
|
|
25880
|
-
__name(formatCredits, "formatCredits");
|
|
25881
|
-
|
|
25882
25900
|
// src/tools/conversational-review.ts
|
|
25883
25901
|
var PRODUCT_REFERENCE_LABELS = {
|
|
25884
25902
|
same_product: "Es el mismo producto",
|
|
@@ -26208,29 +26226,29 @@ ${parsed.data.error ? `${escapeMarkdownText(parsed.data.error)}
|
|
|
26208
26226
|
__name(renderVideoScriptMarkdown, "renderVideoScriptMarkdown");
|
|
26209
26227
|
|
|
26210
26228
|
// src/tools/media-presentation.ts
|
|
26211
|
-
import { z as
|
|
26212
|
-
var HttpsPresentationUrlSchema =
|
|
26229
|
+
import { z as z118 } from "zod";
|
|
26230
|
+
var HttpsPresentationUrlSchema = z118.string().url().max(2048).refine((value) => new URL(value).protocol === "https:");
|
|
26213
26231
|
var ProductMakerPreviewUrlSchema = HttpsPresentationUrlSchema.refine((value) => {
|
|
26214
26232
|
const hostname = new URL(value).hostname.toLowerCase();
|
|
26215
26233
|
return hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
|
|
26216
26234
|
}, "preview URL must be owned by ProductMaker");
|
|
26217
|
-
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) =>
|
|
26218
|
-
var PresentationImageSchema =
|
|
26219
|
-
kind:
|
|
26235
|
+
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) => z118.number().int().positive().max(maximum), "positiveIntAtMost");
|
|
26236
|
+
var PresentationImageSchema = z118.object({
|
|
26237
|
+
kind: z118.literal("image"),
|
|
26220
26238
|
url: HttpsPresentationUrlSchema,
|
|
26221
|
-
mimeType:
|
|
26239
|
+
mimeType: z118.enum([
|
|
26222
26240
|
"image/jpeg",
|
|
26223
26241
|
"image/png"
|
|
26224
26242
|
]),
|
|
26225
26243
|
byteSize: positiveIntAtMost(5 * 1024 * 1024),
|
|
26226
26244
|
width: positiveIntAtMost(16384),
|
|
26227
26245
|
height: positiveIntAtMost(16384),
|
|
26228
|
-
alt:
|
|
26246
|
+
alt: z118.string().trim().min(1).max(240)
|
|
26229
26247
|
}).strict();
|
|
26230
26248
|
var McpMediaPresentationSchema = PresentationImageSchema;
|
|
26231
|
-
var GalleryImageSchema =
|
|
26249
|
+
var GalleryImageSchema = z118.object({
|
|
26232
26250
|
url: ProductMakerPreviewUrlSchema,
|
|
26233
|
-
alt:
|
|
26251
|
+
alt: z118.string().trim().min(1).max(240)
|
|
26234
26252
|
});
|
|
26235
26253
|
var MCP_GALLERY_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
26236
26254
|
async function imageBlocks(image, transport) {
|
|
@@ -26287,12 +26305,12 @@ async function buildMcpMediaPresentationContent(rawPresentation, transport = "ht
|
|
|
26287
26305
|
];
|
|
26288
26306
|
}
|
|
26289
26307
|
__name(buildMcpMediaPresentationContent, "buildMcpMediaPresentationContent");
|
|
26290
|
-
var OwnedImagePreviewSchema =
|
|
26308
|
+
var OwnedImagePreviewSchema = z118.object({
|
|
26291
26309
|
url: HttpsPresentationUrlSchema.refine((value) => {
|
|
26292
26310
|
const url = new URL(value);
|
|
26293
26311
|
return !url.username && !url.password && !isPrivateHost(url.hostname);
|
|
26294
26312
|
}, "Preview URL must be public HTTPS without credentials"),
|
|
26295
|
-
alt:
|
|
26313
|
+
alt: z118.string().trim().min(1).max(240)
|
|
26296
26314
|
});
|
|
26297
26315
|
async function buildMcpImagePreviewContent(rawPreview, transport = "http") {
|
|
26298
26316
|
const preview2 = OwnedImagePreviewSchema.safeParse(rawPreview);
|
|
@@ -26375,7 +26393,7 @@ async function buildMcpImageGalleryContent(rawItems, transport = "http") {
|
|
|
26375
26393
|
__name(buildMcpImageGalleryContent, "buildMcpImageGalleryContent");
|
|
26376
26394
|
|
|
26377
26395
|
// src/tools/animated-video.ts
|
|
26378
|
-
import { z as
|
|
26396
|
+
import { z as z119 } from "zod";
|
|
26379
26397
|
|
|
26380
26398
|
// src/tools/paid-confirmation.ts
|
|
26381
26399
|
function describeMarketStudyChoice(enabled) {
|
|
@@ -26624,29 +26642,29 @@ function hookVariantsLabel(count) {
|
|
|
26624
26642
|
return count === 1 ? "1 versi\xF3n del gancho" : `${count} versiones del gancho`;
|
|
26625
26643
|
}
|
|
26626
26644
|
__name(hookVariantsLabel, "hookVariantsLabel");
|
|
26627
|
-
var DurationInput =
|
|
26628
|
-
var CreateAnimatedVideoInput =
|
|
26629
|
-
action:
|
|
26645
|
+
var DurationInput = z119.union(MOTION_GRAPHICS_DURATIONS.map((d) => z119.literal(d)));
|
|
26646
|
+
var CreateAnimatedVideoInput = z119.object({
|
|
26647
|
+
action: z119.enum([
|
|
26630
26648
|
"prepare",
|
|
26631
26649
|
"status",
|
|
26632
26650
|
"cancel",
|
|
26633
26651
|
"generate"
|
|
26634
26652
|
]).default("prepare").describe("prepare inicia una captura gratuita; status consulta websiteCaptureId; cancel la cancela; generate requiere captura ready y confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrar vista previa y precio."),
|
|
26635
|
-
websiteCaptureId:
|
|
26636
|
-
generationRequestId:
|
|
26637
|
-
confirmed:
|
|
26653
|
+
websiteCaptureId: z119.string().uuid().optional().describe("ID de la captura preparada. Obligatorio para status/cancel/generate; no inventarlo."),
|
|
26654
|
+
generationRequestId: z119.string().uuid().optional().describe("Identidad opcional de una generaci\xF3n confirmada. Repite el mismo UUID al recuperar una respuesta perdida; usa otro \xFAnicamente cuando el usuario pida y confirme otro video, nunca por un error de transporte."),
|
|
26655
|
+
confirmed: z119.boolean().optional().describe("Solo true despu\xE9s de que el usuario confirme esta captura, formato, duraci\xF3n, idioma y precio. Obligatorio para generate; preparar no genera ni cobra."),
|
|
26638
26656
|
// Plain text, not `.url()`: users type «cal.com», and the web accepts it.
|
|
26639
26657
|
// normalizeWebAddress (shared with the web) adds https:// and refuses
|
|
26640
26658
|
// anything that is not a public http(s) address.
|
|
26641
|
-
landingUrl:
|
|
26659
|
+
landingUrl: z119.string().trim().min(1).optional().describe("Enlace p\xFAblico de la p\xE1gina del servicio, como lo dio el usuario (\xABcal.com\xBB o \xABhttps://cal.com\xBB). El video sale de lo que muestra esa p\xE1gina: su marca, colores y mensajes."),
|
|
26642
26660
|
format: MotionGraphicsFormatSchema.optional().describe('Formato: "vertical" (9:16, Reels/TikTok), "square" (1:1, feed) u "horizontal" (16:9, YouTube/web). Si el usuario no lo dijo, PREG\xDANTALE.'),
|
|
26643
26661
|
durationSec: DurationInput.optional().describe(`Duraci\xF3n en segundos: ${MOTION_GRAPHICS_DURATIONS.join(", ")}. Fija el precio (ver la descripci\xF3n de la herramienta). Si el usuario no la dijo, PREG\xDANTALE.`),
|
|
26644
26662
|
language: MotionGraphicsLanguageSchema.optional().describe('Idioma de los textos del video: "es", "en" o "pt".'),
|
|
26645
26663
|
videoQuality: MotionGraphicsVideoQualitySchema.default(DEFAULT_MOTION_GRAPHICS_VIDEO_QUALITY).describe('Calidad IA: "economy" (econ\xF3mica) o "pro" (alta, por defecto). Afecta el precio de los clips IA.'),
|
|
26646
26664
|
videoResolution: MotionGraphicsVideoResolutionSchema.default(DEFAULT_MOTION_GRAPHICS_VIDEO_RESOLUTION).describe('Resoluci\xF3n: "480p" (por defecto), "720p" o "1080p". 1080p solo est\xE1 disponible con videoQuality="pro".'),
|
|
26647
|
-
notes:
|
|
26665
|
+
notes: z119.string().trim().max(MOTION_GRAPHICS_NOTES_MAX_CHARS).optional().describe("Opcional. Idea creativa, escenarios, acciones, di\xE1logo, voz o m\xFAsica en lenguaje natural. El director puede hacer solo motion graphics, escenas reales o escenas imposibles seg\xFAn la idea; las frases gu\xEDan el guion sin garantizar reproducci\xF3n exacta."),
|
|
26648
26666
|
hookVariants: MotionGraphicsHookVariantsSchema.optional().describe(`Opcional. Versiones del gancho: el mismo video con distintas aperturas para probar cu\xE1l funciona mejor, de 1 a ${MOTION_GRAPHICS_HOOK_VARIANTS_MAX} (por defecto 1). Cada versi\xF3n extra tiene precio (ver la descripci\xF3n de la herramienta); \xFAsalo solo si el usuario lo pide.`),
|
|
26649
|
-
music:
|
|
26667
|
+
music: z119.boolean().optional().describe("Opcional. M\xFAsica y efectos dirigidos seg\xFAn el negocio y el video (por defecto true), con una copia adicional solo con voces. false entrega solo voces, sin m\xFAsica ni efectos a\xF1adidos ni el cargo de m\xFAsica. \xDAsalo solo si el usuario lo pide.")
|
|
26650
26668
|
}).strict();
|
|
26651
26669
|
function captureNextAction(capture) {
|
|
26652
26670
|
if (capture.status === "ready") return "Muestra la vista previa al usuario y confirma formato, duraci\xF3n, idioma, calidad IA, resoluci\xF3n y precio. Solo despu\xE9s llama create_animated_video con action=generate, este websiteCaptureId y confirmed=true.";
|
|
@@ -26696,49 +26714,49 @@ var FORMAT_LABELS = {
|
|
|
26696
26714
|
square: "cuadrado 1:1",
|
|
26697
26715
|
horizontal: "horizontal 16:9"
|
|
26698
26716
|
};
|
|
26699
|
-
var AnimatedVideoResultSchema =
|
|
26700
|
-
status:
|
|
26717
|
+
var AnimatedVideoResultSchema = z119.object({
|
|
26718
|
+
status: z119.enum([
|
|
26701
26719
|
"pending",
|
|
26702
26720
|
"done",
|
|
26703
26721
|
"error"
|
|
26704
26722
|
]),
|
|
26705
|
-
serviceMode:
|
|
26706
|
-
finalMp4Url:
|
|
26707
|
-
adCopy:
|
|
26708
|
-
adCopies:
|
|
26709
|
-
error:
|
|
26710
|
-
motionGraphics:
|
|
26723
|
+
serviceMode: z119.literal("motion_graphics"),
|
|
26724
|
+
finalMp4Url: z119.string().optional(),
|
|
26725
|
+
adCopy: z119.string().optional(),
|
|
26726
|
+
adCopies: z119.array(z119.string()).optional(),
|
|
26727
|
+
error: z119.string().optional(),
|
|
26728
|
+
motionGraphics: z119.object({
|
|
26711
26729
|
stage: MotionGraphicsStageSchema,
|
|
26712
26730
|
format: MotionGraphicsFormatSchema,
|
|
26713
|
-
durationSec:
|
|
26714
|
-
width:
|
|
26715
|
-
height:
|
|
26731
|
+
durationSec: z119.number(),
|
|
26732
|
+
width: z119.number().optional(),
|
|
26733
|
+
height: z119.number().optional(),
|
|
26716
26734
|
language: MotionGraphicsLanguageSchema.optional(),
|
|
26717
26735
|
videoQuality: MotionGraphicsVideoQualitySchema.optional().catch(void 0),
|
|
26718
26736
|
videoResolution: MotionGraphicsVideoResolutionSchema.optional().catch(void 0),
|
|
26719
|
-
queuedAt:
|
|
26720
|
-
jobsAhead:
|
|
26721
|
-
stageStartedAt:
|
|
26722
|
-
renderProgress:
|
|
26737
|
+
queuedAt: z119.string().optional(),
|
|
26738
|
+
jobsAhead: z119.number().int().nonnegative().optional(),
|
|
26739
|
+
stageStartedAt: z119.string().optional(),
|
|
26740
|
+
renderProgress: z119.number().optional(),
|
|
26723
26741
|
// AI clip fields (absent on a video paid before clips, or from an API
|
|
26724
26742
|
// that predates them). Lenient: an unreadable value is dropped
|
|
26725
26743
|
// instead of hiding the status.
|
|
26726
|
-
clipCount:
|
|
26727
|
-
clipsUsed:
|
|
26728
|
-
clipPriceCents:
|
|
26729
|
-
clipsRefunded:
|
|
26744
|
+
clipCount: z119.number().int().nonnegative().optional().catch(void 0),
|
|
26745
|
+
clipsUsed: z119.number().int().nonnegative().optional().catch(void 0),
|
|
26746
|
+
clipPriceCents: z119.number().finite().nonnegative().optional().catch(void 0),
|
|
26747
|
+
clipsRefunded: z119.number().int().nonnegative().optional().catch(void 0),
|
|
26730
26748
|
// Hook versions (absent from an API or a video before them): the
|
|
26731
26749
|
// count ordered and, once done, the signed URLs of the EXTRA versions
|
|
26732
26750
|
// (hook 2, hook 3); `finalMp4Url` is hook 1.
|
|
26733
|
-
hookVariants:
|
|
26734
|
-
cleanVideoUrl:
|
|
26735
|
-
cleanVariantUrls:
|
|
26736
|
-
variantUrls:
|
|
26751
|
+
hookVariants: z119.number().int().min(1).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX).optional().catch(void 0),
|
|
26752
|
+
cleanVideoUrl: z119.string().url().optional().catch(void 0),
|
|
26753
|
+
cleanVariantUrls: z119.array(z119.string().url()).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX - 1).optional().catch(void 0),
|
|
26754
|
+
variantUrls: z119.array(z119.string().min(1)).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX - 1).optional().catch(void 0),
|
|
26737
26755
|
// Extra hook versions given back, as the task recorded it once the
|
|
26738
26756
|
// refunds landed (absent until then, and from an API before it).
|
|
26739
|
-
variantsRefunded:
|
|
26740
|
-
activity:
|
|
26741
|
-
previewUrl:
|
|
26757
|
+
variantsRefunded: z119.number().int().nonnegative().optional().catch(void 0),
|
|
26758
|
+
activity: z119.array(MotionGraphicsActivitySchema).optional().catch(void 0),
|
|
26759
|
+
previewUrl: z119.string().optional()
|
|
26742
26760
|
}).passthrough()
|
|
26743
26761
|
}).passthrough();
|
|
26744
26762
|
function parseAnimatedVideoResult(value) {
|
|
@@ -27041,10 +27059,10 @@ function registerAnimatedVideoTools(server, api) {
|
|
|
27041
27059
|
__name(registerAnimatedVideoTools, "registerAnimatedVideoTools");
|
|
27042
27060
|
|
|
27043
27061
|
// src/tools/winning-product-image.ts
|
|
27044
|
-
import { z as
|
|
27045
|
-
var WinnerReferenceInput =
|
|
27046
|
-
runId:
|
|
27047
|
-
candidateSourceId:
|
|
27062
|
+
import { z as z120 } from "zod";
|
|
27063
|
+
var WinnerReferenceInput = z120.object({
|
|
27064
|
+
runId: z120.string().uuid(),
|
|
27065
|
+
candidateSourceId: z120.string().trim().min(1).max(160)
|
|
27048
27066
|
}).strict().describe("Referencia opaca de un producto devuelto por find_winning_products/get_product_research_run.");
|
|
27049
27067
|
async function resolveWinnerImages(api, winnerReference, images) {
|
|
27050
27068
|
if (!winnerReference) return images;
|
|
@@ -27081,62 +27099,62 @@ var createProductTaskSingleOutputDescription = renderCreateProductTaskAvoidWhenF
|
|
|
27081
27099
|
var CREATE_SERVICE_VIDEO_ON_MCP = PRODUCTMAKER_MCP_CAPABILITY_NAMES.includes("create_service_video");
|
|
27082
27100
|
var listTasksGuidance = getProductMakerCapabilityModelGuidance("list_tasks");
|
|
27083
27101
|
var editTaskDraftGuidance = getProductMakerCapabilityModelGuidance("edit_task_draft");
|
|
27084
|
-
var CreateProductTaskInput =
|
|
27102
|
+
var CreateProductTaskInput = z121.object({
|
|
27085
27103
|
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
27086
27104
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
27087
|
-
projectId:
|
|
27088
|
-
productUrl:
|
|
27105
|
+
projectId: z121.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
|
|
27106
|
+
productUrl: z121.string().url().optional().describe("Opcional. URL de la p\xE1gina del producto (Shopify, AliExpress, Amazon, etc.) para enriquecer el contexto. Si se omite, la IA infiere todo desde la imagen."),
|
|
27089
27107
|
language: Lang.describe('C\xF3digo de idioma para el contenido generado. Ejemplos: "es", "en", "pt". REQUERIDO \u2014 si el usuario no lo especific\xF3, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas "es" por default.'),
|
|
27090
27108
|
country: Country.describe('C\xF3digo ISO del pa\xEDs objetivo. Ejemplos: "CO", "MX", "US", "AR". Define moneda, m\xE9todos de pago locales y tono. REQUERIDO \u2014 si el usuario no lo especific\xF3, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat.'),
|
|
27091
|
-
price:
|
|
27092
|
-
compareAtPrice:
|
|
27093
|
-
offer:
|
|
27094
|
-
paymentMethods:
|
|
27095
|
-
productContext:
|
|
27096
|
-
productLabel:
|
|
27109
|
+
price: z121.string().min(1).max(120).optional().describe('Precio que se mostrar\xE1 en la landing y video. Formato libre \u2014 ej. "$89.900", "USD 29.99", "MXN 599". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar. Si te dice "t\xFA elige", om\xEDtelo y el backend sugiere uno.'),
|
|
27110
|
+
compareAtPrice: z121.string().min(1).max(120).optional().describe('Opcional. Precio anterior para comunicar un descuento real en landing e im\xE1genes. Ej. "$129.000" cuando price="$89.900".'),
|
|
27111
|
+
offer: z121.string().min(1).max(240).optional().describe('Oferta o promoci\xF3n a destacar. Ej. "2x1", "Env\xEDo gratis hoy", "50% OFF". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar (es v\xE1lido que diga "sin oferta"). Si te dice "t\xFA elige", om\xEDtelo.'),
|
|
27112
|
+
paymentMethods: z121.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago que puede mostrar la landing. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
|
|
27113
|
+
productContext: z121.string().max(PRODUCT_CONTEXT_MAX_CHARS).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
|
|
27114
|
+
productLabel: z121.string().trim().max(80).optional().describe('Opcional. Qu\xE9 es el producto, en pocas palabras (ej. "quita pelusas para ropa"). Es AUTORITATIVO: si lo env\xEDas, la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA detecta el producto desde la imagen.'),
|
|
27097
27115
|
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s (single_call) y 30s (Multiescena); "problem_solution" en single_call abre sin el producto y el producto entra por referencia en su beat de soluci\xF3n. Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "problem_solution" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (idea imposible: un truco visual que en la vida real no podr\xEDa pasar, y al cierre la invitaci\xF3n a comprar), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla). Si el usuario pide algo fuera de esta lista, p\xEDdele una opci\xF3n v\xE1lida.'),
|
|
27098
27116
|
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.'),
|
|
27099
|
-
durationSeconds:
|
|
27100
|
-
|
|
27101
|
-
|
|
27117
|
+
durationSeconds: z121.union([
|
|
27118
|
+
z121.literal(15),
|
|
27119
|
+
z121.literal(30)
|
|
27102
27120
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
27103
|
-
pauseForAngleReview:
|
|
27104
|
-
aiDecidesAll:
|
|
27121
|
+
pauseForAngleReview: z121.boolean().optional().describe("Compatibilidad heredada. MCP deriva esta pausa de aiDecidesAll: manual siempre pausa y autom\xE1tico siempre la omite. No uses este campo para saltar reviews."),
|
|
27122
|
+
aiDecidesAll: z121.boolean().optional().describe("Mapea el toggle web \u201CDejar que la IA decida todo\u201D. Si true, ProductMaker omite reviews. Act\xEDvalo \xFAnicamente cuando el usuario lo pida expl\xEDcitamente. Omitido/false = reviews conversacionales."),
|
|
27105
27123
|
videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista" para 15s; al pedir durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
|
|
27106
27124
|
videoStructure: VideoStructureEnum.default("single_take").describe('Opcional. Formato: "single_take" = Un solo clip (generado completo; puede incluir cambios de plano) o "multi_scene" = Multiescena. Default: single_take.'),
|
|
27107
27125
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
27108
27126
|
videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "480p" = excelente calidad y m\xE1s econ\xF3mico (RECOMENDADO, default si se omite); "720p" = m\xE1xima nitidez, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
|
|
27109
|
-
videoAngleCount:
|
|
27127
|
+
videoAngleCount: z121.number().int().min(1).max(3).optional().describe("Cantidad de videos a renderizar: 1, 2 o 3. REQUERIDO cuando outputs incluye video (o se omite outputs, cuyo default incluye video); om\xEDtelo s\xF3lo cuando no se generar\xE1 video. Preg\xFAntalo incluso con aiDecidesAll=true porque cambia el costo."),
|
|
27110
27128
|
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.'),
|
|
27111
|
-
outputs:
|
|
27129
|
+
outputs: z121.array(z121.enum([
|
|
27112
27130
|
"landing",
|
|
27113
27131
|
"video",
|
|
27114
27132
|
"image"
|
|
27115
27133
|
])).min(1).max(3).optional().describe(`Opcional. Qu\xE9 outputs generar. Default: todos ["landing","video","image"]. Para 2 outputs: ["landing","image"] (sin video), ["video","image"] (sin landing), ["landing","video"] (sin imagen). ${createProductTaskSingleOutputDescription}`),
|
|
27116
|
-
includeInitialLandingGif:
|
|
27117
|
-
competitorScanEnabled:
|
|
27134
|
+
includeInitialLandingGif: z121.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."),
|
|
27135
|
+
competitorScanEnabled: z121.boolean().optional().describe("Opcional. Default false: omitirlo significa SIN estudio de mercado. Antes de crear, llama estimate_market_study_cost con kind=product_task, 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). Si la tarea incluye video, true analiza anuncios de la competencia y desbloquea la narrativa an\xE1lisis de mercado; si el estudio no encuentra datos suficientes, degrada al planner cl\xE1sico."),
|
|
27118
27136
|
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.'),
|
|
27119
|
-
replicateWinnerWhenFound:
|
|
27120
|
-
brandId:
|
|
27121
|
-
actorId:
|
|
27137
|
+
replicateWinnerWhenFound: z121.boolean().optional().describe("Conserva la preferencia de copiar un ganador si existe. false estudia el mercado sin reemplazar un \xE1ngulo por el anuncio ganador. No a\xF1ade una pregunta obligatoria."),
|
|
27138
|
+
brandId: z121.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer los outputs generados."),
|
|
27139
|
+
actorId: z121.string().trim().min(1).max(120).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
|
|
27122
27140
|
creativeGender: CreativeGenderEnum.optional().describe('Opcional. QUI\xC9N APARECE EN PANTALLA en todos los creativos de la task: el presentador del video, las personas fotografiadas en la landing y la persona del anuncio. NO es la voz. M\xE1ndalo en cuanto el usuario lo diga en cualquier momento de la conversaci\xF3n y con cualquier redacci\xF3n ("quiero una mujer", "que salga un hombre", "esto es para hombres"): si no lo mandas, el g\xE9nero lo inventa la IA y el usuario recibe lo contrario de lo que pidi\xF3 sin ning\xFAn aviso. "mixed" solo cuando pida expl\xEDcitamente ambos. Si adem\xE1s mandas actorId, el personaje guardado manda sobre este campo.'),
|
|
27123
|
-
creativeDirection:
|
|
27124
|
-
headline:
|
|
27125
|
-
subheadline:
|
|
27141
|
+
creativeDirection: z121.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo) que aplica a los outputs de IMAGEN y LANDING (no al video \u2014 el estilo/ambiente del video se dirige con scriptNotes). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Ej. "amarillo y azul, minimalista". M\xE1x 500 caracteres.'),
|
|
27142
|
+
headline: z121.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar. Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. Se aplica a las IM\xC1GENES y a la portada de la landing de esta task (el video no lo usa; para el video usa scriptNotes). \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
|
|
27143
|
+
subheadline: z121.string().max(160).optional().describe("Opcional. Subt\xEDtulo EXACTO a renderizar, misma regla que headline. Independiente: puedes fijar solo el subt\xEDtulo. M\xE1x 160 caracteres."),
|
|
27126
27144
|
landingStrategy: LandingStrategyEnum.optional().describe('Opcional. Estructura de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution", "social_proof" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".'),
|
|
27127
27145
|
landingImageFormat: LandingImageFormatEnum.default("9:16").describe('Opcional. Formato/relaci\xF3n de aspecto de las im\xE1genes de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "9:16" (vertical, por defecto), "4:5" (vertical compacto para feed) o "1:1" (cuadrado). Default "9:16".')
|
|
27128
27146
|
}).strict();
|
|
27129
|
-
var CreateServiceVideoInput =
|
|
27130
|
-
landingUrl:
|
|
27131
|
-
additionalContext:
|
|
27147
|
+
var CreateServiceVideoInput = z121.object({
|
|
27148
|
+
landingUrl: z121.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
|
|
27149
|
+
additionalContext: z121.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
|
|
27132
27150
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
27133
|
-
voiceId:
|
|
27134
|
-
brandId:
|
|
27135
|
-
reviewMode:
|
|
27151
|
+
voiceId: z121.string().trim().min(1).pipe(VoiceId).optional().describe('Opcional. Voz para toda la narraci\xF3n. Usa "auto" para que la IA la alinee con el presentador. Las voces del banco compartido fueron retiradas.'),
|
|
27152
|
+
brandId: z121.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
|
|
27153
|
+
reviewMode: z121.enum([
|
|
27136
27154
|
"automatic",
|
|
27137
27155
|
"guided"
|
|
27138
27156
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
27139
|
-
videoPreset:
|
|
27157
|
+
videoPreset: z121.enum([
|
|
27140
27158
|
"auto",
|
|
27141
27159
|
"ugc_dynamic",
|
|
27142
27160
|
"ugc_how_to",
|
|
@@ -27146,128 +27164,128 @@ var CreateServiceVideoInput = z120.object({
|
|
|
27146
27164
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
27147
27165
|
videoResolution: VideoResolutionEnum.default("480p").describe('Opcional. Resoluci\xF3n: "480p" o "720p". 720p cuesta m\xE1s por segundo y solo est\xE1 disponible en "economy" y "pro" (Medio = 768P). Default: 480p.')
|
|
27148
27166
|
}).strict();
|
|
27149
|
-
var GetTaskStatusInput =
|
|
27150
|
-
taskId:
|
|
27151
|
-
waitSeconds:
|
|
27167
|
+
var GetTaskStatusInput = z121.object({
|
|
27168
|
+
taskId: z121.string().min(1),
|
|
27169
|
+
waitSeconds: z121.number().int().min(0).max(120).optional()
|
|
27152
27170
|
});
|
|
27153
|
-
var ListTasksInput =
|
|
27154
|
-
limit:
|
|
27155
|
-
offset:
|
|
27156
|
-
search:
|
|
27171
|
+
var ListTasksInput = z121.object({
|
|
27172
|
+
limit: z121.number().int().min(1).max(50).optional(),
|
|
27173
|
+
offset: z121.number().int().min(0).optional(),
|
|
27174
|
+
search: z121.string().optional()
|
|
27157
27175
|
});
|
|
27158
27176
|
var MCP_REVIEW_LIST_FIELD_MAX = IMAGE_WIZARD_LIST_FIELD_MAX;
|
|
27159
27177
|
var MCP_REVIEW_FEATURE_ITEM_MAX = IMAGE_FEATURE_ITEM_MAX;
|
|
27160
|
-
var EditTaskDraftInput =
|
|
27161
|
-
taskId:
|
|
27162
|
-
angleIndex:
|
|
27163
|
-
angle:
|
|
27164
|
-
angle:
|
|
27165
|
-
buyerPersona:
|
|
27166
|
-
actor:
|
|
27167
|
-
actorId:
|
|
27168
|
-
gender:
|
|
27178
|
+
var EditTaskDraftInput = z121.object({
|
|
27179
|
+
taskId: z121.string().min(1),
|
|
27180
|
+
angleIndex: z121.number().int().min(0),
|
|
27181
|
+
angle: z121.object({
|
|
27182
|
+
angle: z121.string().min(1).max(480),
|
|
27183
|
+
buyerPersona: z121.string().min(1).max(360),
|
|
27184
|
+
actor: z121.string().min(1).max(360).optional(),
|
|
27185
|
+
actorId: z121.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
|
|
27186
|
+
gender: z121.enum([
|
|
27169
27187
|
"female",
|
|
27170
27188
|
"male"
|
|
27171
27189
|
]).optional(),
|
|
27172
|
-
scriptNotes:
|
|
27190
|
+
scriptNotes: z121.string().max(2e3).optional()
|
|
27173
27191
|
})
|
|
27174
27192
|
});
|
|
27175
|
-
var PendingActionPayload =
|
|
27176
|
-
|
|
27177
|
-
kind:
|
|
27193
|
+
var PendingActionPayload = z121.discriminatedUnion("kind", [
|
|
27194
|
+
z121.object({
|
|
27195
|
+
kind: z121.literal("product_reference_set_approval"),
|
|
27178
27196
|
scope: ProductReferenceScopeEnum.optional(),
|
|
27179
|
-
scopeKey:
|
|
27180
|
-
scopeSnapshotId:
|
|
27181
|
-
expectedScopeHash:
|
|
27197
|
+
scopeKey: z121.string().min(1).max(120).optional(),
|
|
27198
|
+
scopeSnapshotId: z121.string().uuid().optional(),
|
|
27199
|
+
expectedScopeHash: z121.string().min(1).max(128).optional(),
|
|
27182
27200
|
decision: ProductReferenceResolutionDecisionEnum,
|
|
27183
27201
|
reason: ProductReferenceRejectionReasonEnum.optional(),
|
|
27184
|
-
affectedRefKeys:
|
|
27185
|
-
affectedPhotoNumbers:
|
|
27186
|
-
recoveryAction:
|
|
27202
|
+
affectedRefKeys: z121.array(z121.string().min(1).max(200)).min(1).optional(),
|
|
27203
|
+
affectedPhotoNumbers: z121.array(z121.number().int().positive()).min(1).optional(),
|
|
27204
|
+
recoveryAction: z121.enum([
|
|
27187
27205
|
"remove",
|
|
27188
27206
|
"replace",
|
|
27189
27207
|
"upload",
|
|
27190
27208
|
"return_to_selection"
|
|
27191
27209
|
]).optional()
|
|
27192
27210
|
}),
|
|
27193
|
-
|
|
27194
|
-
kind:
|
|
27195
|
-
selectedPhotoNumbers:
|
|
27211
|
+
z121.object({
|
|
27212
|
+
kind: z121.literal("product_reference_confirmation"),
|
|
27213
|
+
selectedPhotoNumbers: z121.array(z121.number().int().positive().max(5)).min(1).max(5).optional().describe("Opcional. N\xFAmeros visibles de las fotos que el usuario quiere conservar; las dem\xE1s se deseleccionan. Debe quedar al menos una."),
|
|
27196
27214
|
userChoice: ProductReferenceUserChoiceEnum.optional(),
|
|
27197
|
-
confirmedProductNames:
|
|
27215
|
+
confirmedProductNames: z121.array(z121.string().min(1).max(120)).min(2).max(8).optional(),
|
|
27198
27216
|
videoStructure: VideoStructureEnum.optional().describe("Opcional. Formato para reanudar el video: single_take (Un solo clip, generado completo; puede incluir cambios de plano) o multi_scene (Multiescena). El backend vuelve a validar que sea compatible con el agrupamiento confirmado."),
|
|
27199
27217
|
videoQuality: VideoQualityEnum.optional().describe("Opcional. Calidad para reanudar el video: economy (Econ\xF3mico), medium (Medio) o pro. El proveedor se elige internamente y el backend vuelve a cotizar la ruta."),
|
|
27200
|
-
useMultiSceneShowcase:
|
|
27218
|
+
useMultiSceneShowcase: z121.boolean().optional().describe("Compatibilidad con confirmaciones antiguas: si es true junto con userChoice=variants, conserva el showcase multiescena.")
|
|
27201
27219
|
}),
|
|
27202
|
-
|
|
27203
|
-
kind:
|
|
27220
|
+
z121.object({
|
|
27221
|
+
kind: z121.literal("angle_review_approval"),
|
|
27204
27222
|
// All edits are optional — empty action body just approves the
|
|
27205
27223
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
27206
27224
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
27207
27225
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
27208
|
-
customAngle:
|
|
27226
|
+
customAngle: z121.string().min(1).max(480).optional(),
|
|
27209
27227
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
27210
27228
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
27211
|
-
painPoints:
|
|
27212
|
-
benefits:
|
|
27229
|
+
painPoints: z121.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
27230
|
+
benefits: z121.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
27213
27231
|
// Same policy as the two fields above: the strictest downstream contract
|
|
27214
27232
|
// wins. The image endpoint caps each feature at 240 while landing/video
|
|
27215
27233
|
// accept 300, and this single action routes to whichever the task is — so
|
|
27216
27234
|
// accepting 300 here turns a 250-character feature on an image task into
|
|
27217
27235
|
// an opaque HTTP 400 after the model already believed it had been saved.
|
|
27218
|
-
features:
|
|
27219
|
-
buyerPersona:
|
|
27236
|
+
features: z121.array(z121.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
|
|
27237
|
+
buyerPersona: z121.string().min(1).max(360).optional(),
|
|
27220
27238
|
narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora.'),
|
|
27221
|
-
actor:
|
|
27222
|
-
actorId:
|
|
27223
|
-
creativeDirection:
|
|
27224
|
-
selectedAngleNumbers:
|
|
27225
|
-
angleEdits:
|
|
27226
|
-
angleNumber:
|
|
27227
|
-
narrativeSelection:
|
|
27239
|
+
actor: z121.string().min(1).max(360).optional().describe("Nueva descripci\xF3n del presentador. Al enviarla sin actorId, se elimina autom\xE1ticamente cualquier actor guardado previo de esos \xE1ngulos."),
|
|
27240
|
+
actorId: z121.string().max(120).nullable().optional(),
|
|
27241
|
+
creativeDirection: z121.string().max(2e3).optional(),
|
|
27242
|
+
selectedAngleNumbers: z121.array(z121.number().int().positive()).min(1).max(3).optional().describe("N\xFAmeros visibles (1-based) de los \xE1ngulos que el usuario quiere renderizar. Deben ser \xFAnicos, existir en la review y no superar el videoAngleCount autorizado."),
|
|
27243
|
+
angleEdits: z121.array(z121.object({
|
|
27244
|
+
angleNumber: z121.number().int().positive(),
|
|
27245
|
+
narrativeSelection: z121.enum([
|
|
27228
27246
|
"market",
|
|
27229
27247
|
"guided"
|
|
27230
27248
|
]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo."),
|
|
27231
|
-
angle:
|
|
27232
|
-
buyerPersona:
|
|
27249
|
+
angle: z121.string().min(1).max(480).optional(),
|
|
27250
|
+
buyerPersona: z121.string().min(1).max(360).optional(),
|
|
27233
27251
|
narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora.'),
|
|
27234
|
-
actor:
|
|
27235
|
-
actorId:
|
|
27236
|
-
creativeDirection:
|
|
27252
|
+
actor: z121.string().min(1).max(360).optional(),
|
|
27253
|
+
actorId: z121.string().max(120).nullable().optional(),
|
|
27254
|
+
creativeDirection: z121.string().max(2e3).optional()
|
|
27237
27255
|
}).strict()).max(3).optional().describe("Ediciones independientes por n\xFAmero visible de \xE1ngulo. S\xF3lo se aceptan para \xE1ngulos incluidos en selectedAngleNumbers (o en la selecci\xF3n por defecto)."),
|
|
27238
|
-
gapDurationSeconds:
|
|
27239
|
-
|
|
27240
|
-
|
|
27256
|
+
gapDurationSeconds: z121.union([
|
|
27257
|
+
z121.literal(15),
|
|
27258
|
+
z121.literal(30)
|
|
27241
27259
|
]).optional().describe("Opcional. Solo para aceptar expl\xEDcitamente la recomendaci\xF3n gapDurationChoice del status. 30 env\xEDa el override double_30s para el \xE1ngulo recomendado; 15 u omitido conserva la duraci\xF3n cotizada.")
|
|
27242
27260
|
}),
|
|
27243
|
-
|
|
27244
|
-
kind:
|
|
27245
|
-
angleId:
|
|
27246
|
-
angleNumber:
|
|
27247
|
-
content:
|
|
27248
|
-
creativeDirection:
|
|
27249
|
-
actor:
|
|
27261
|
+
z121.object({
|
|
27262
|
+
kind: z121.literal("service_angle_approval"),
|
|
27263
|
+
angleId: z121.string().uuid().optional(),
|
|
27264
|
+
angleNumber: z121.number().int().positive().optional(),
|
|
27265
|
+
content: z121.string().min(1).max(480).optional(),
|
|
27266
|
+
creativeDirection: z121.string().min(1).max(1500).optional(),
|
|
27267
|
+
actor: z121.string().min(1).max(360).optional()
|
|
27250
27268
|
}),
|
|
27251
|
-
|
|
27252
|
-
kind:
|
|
27253
|
-
voiceovers:
|
|
27254
|
-
visualDirections:
|
|
27255
|
-
voiceoversByScene:
|
|
27256
|
-
visualDirectionsByScene:
|
|
27269
|
+
z121.object({
|
|
27270
|
+
kind: z121.literal("service_script_approval"),
|
|
27271
|
+
voiceovers: z121.record(z121.string(), z121.string().min(1).max(600)).optional(),
|
|
27272
|
+
visualDirections: z121.record(z121.string(), z121.string().min(1).max(1200)).optional(),
|
|
27273
|
+
voiceoversByScene: z121.record(z121.string(), z121.string().min(1).max(600)).optional(),
|
|
27274
|
+
visualDirectionsByScene: z121.record(z121.string(), z121.string().min(1).max(1200)).optional()
|
|
27257
27275
|
})
|
|
27258
27276
|
]);
|
|
27259
|
-
var ResolvePendingActionInput =
|
|
27260
|
-
taskId:
|
|
27277
|
+
var ResolvePendingActionInput = z121.object({
|
|
27278
|
+
taskId: z121.string().min(1),
|
|
27261
27279
|
action: PendingActionPayload
|
|
27262
27280
|
}).strict();
|
|
27263
27281
|
function resolveVariantConfirmVideoTier(action) {
|
|
27264
27282
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
27265
27283
|
}
|
|
27266
27284
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
27267
|
-
var StatusForResolveSchema =
|
|
27285
|
+
var StatusForResolveSchema = z121.object({
|
|
27268
27286
|
// Metadata used to select the backend resume contract. Older API responses
|
|
27269
27287
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
27270
|
-
kind:
|
|
27288
|
+
kind: z121.enum([
|
|
27271
27289
|
"autopilot",
|
|
27272
27290
|
"video",
|
|
27273
27291
|
"image",
|
|
@@ -27275,18 +27293,18 @@ var StatusForResolveSchema = z120.object({
|
|
|
27275
27293
|
"mix",
|
|
27276
27294
|
"service"
|
|
27277
27295
|
]).optional(),
|
|
27278
|
-
selectedOutputs:
|
|
27296
|
+
selectedOutputs: z121.array(z121.enum([
|
|
27279
27297
|
"landing",
|
|
27280
27298
|
"video",
|
|
27281
27299
|
"image"
|
|
27282
27300
|
])).optional(),
|
|
27283
|
-
videoAngleCount:
|
|
27284
|
-
ugcFormat:
|
|
27301
|
+
videoAngleCount: z121.number().int().min(1).max(3).optional(),
|
|
27302
|
+
ugcFormat: z121.enum([
|
|
27285
27303
|
"single_8s",
|
|
27286
27304
|
"single_15s",
|
|
27287
27305
|
"double_30s"
|
|
27288
27306
|
]).optional(),
|
|
27289
|
-
videoStyle:
|
|
27307
|
+
videoStyle: z121.enum([
|
|
27290
27308
|
"ugc",
|
|
27291
27309
|
"cartoon_3d",
|
|
27292
27310
|
"product_only",
|
|
@@ -27296,13 +27314,13 @@ var StatusForResolveSchema = z120.object({
|
|
|
27296
27314
|
videoQuality: VideoQualityEnum.optional(),
|
|
27297
27315
|
videoRouteId: VideoRouteIdEnum.optional(),
|
|
27298
27316
|
videoModel: TaskVideoModelEnum.optional(),
|
|
27299
|
-
videoTier:
|
|
27317
|
+
videoTier: z121.enum([
|
|
27300
27318
|
"legacy",
|
|
27301
27319
|
"standard",
|
|
27302
27320
|
"pro",
|
|
27303
27321
|
"single_call"
|
|
27304
27322
|
]).optional(),
|
|
27305
|
-
videoResolution:
|
|
27323
|
+
videoResolution: z121.enum([
|
|
27306
27324
|
"480p",
|
|
27307
27325
|
"720p"
|
|
27308
27326
|
]).optional(),
|
|
@@ -27310,60 +27328,60 @@ var StatusForResolveSchema = z120.object({
|
|
|
27310
27328
|
// A task created before a narrative was blocked/hidden must still parse,
|
|
27311
27329
|
// or resolve_pending_action could never approve it.
|
|
27312
27330
|
narrativeStyle: NarrativeStyle.optional(),
|
|
27313
|
-
pauseForVideoScriptReview:
|
|
27314
|
-
pendingUserAction:
|
|
27315
|
-
kind:
|
|
27316
|
-
candidateGrouping:
|
|
27317
|
-
selectedImageUrls:
|
|
27318
|
-
proposedProductNames:
|
|
27331
|
+
pauseForVideoScriptReview: z121.boolean().optional(),
|
|
27332
|
+
pendingUserAction: z121.object({
|
|
27333
|
+
kind: z121.string(),
|
|
27334
|
+
candidateGrouping: z121.string().optional(),
|
|
27335
|
+
selectedImageUrls: z121.array(z121.string()).optional(),
|
|
27336
|
+
proposedProductNames: z121.array(z121.string()).optional(),
|
|
27319
27337
|
// Adaptive reference questions are server-owned. Echo only the evidence
|
|
27320
27338
|
// identity and the choices shown by the current snapshot; never accept a
|
|
27321
27339
|
// client-supplied reference set as authorization.
|
|
27322
|
-
options:
|
|
27323
|
-
mode:
|
|
27340
|
+
options: z121.array(ProductReferenceUserChoiceEnum).max(4).optional(),
|
|
27341
|
+
mode: z121.enum([
|
|
27324
27342
|
"question",
|
|
27325
27343
|
"visual_recovery"
|
|
27326
27344
|
]).optional(),
|
|
27327
27345
|
/** Hand-mirrored from packages/types result.ts — see CLAUDE.md § MCP Server Sync. */
|
|
27328
|
-
canKeepSelection:
|
|
27329
|
-
decisionKey:
|
|
27330
|
-
questionOrdinal:
|
|
27331
|
-
questionCeiling:
|
|
27332
|
-
|
|
27333
|
-
|
|
27346
|
+
canKeepSelection: z121.boolean().optional(),
|
|
27347
|
+
decisionKey: z121.string().optional(),
|
|
27348
|
+
questionOrdinal: z121.number().int().min(1).max(5).optional(),
|
|
27349
|
+
questionCeiling: z121.union([
|
|
27350
|
+
z121.literal(3),
|
|
27351
|
+
z121.literal(5)
|
|
27334
27352
|
]).optional(),
|
|
27335
|
-
referenceEvidenceVersion:
|
|
27336
|
-
expectedEvidenceHash:
|
|
27337
|
-
question:
|
|
27338
|
-
prompt:
|
|
27339
|
-
helpText:
|
|
27340
|
-
options:
|
|
27353
|
+
referenceEvidenceVersion: z121.number().int().positive().optional(),
|
|
27354
|
+
expectedEvidenceHash: z121.string().optional(),
|
|
27355
|
+
question: z121.object({
|
|
27356
|
+
prompt: z121.string().min(1).max(240),
|
|
27357
|
+
helpText: z121.string().min(1).max(300).optional(),
|
|
27358
|
+
options: z121.array(z121.object({
|
|
27341
27359
|
value: ProductReferenceUserChoiceEnum,
|
|
27342
|
-
label:
|
|
27343
|
-
description:
|
|
27360
|
+
label: z121.string().min(1).max(120),
|
|
27361
|
+
description: z121.string().min(1).max(240).optional()
|
|
27344
27362
|
})).min(2).max(4)
|
|
27345
27363
|
}).optional(),
|
|
27346
27364
|
scope: ProductReferenceScopeEnum.optional(),
|
|
27347
|
-
scopeKey:
|
|
27348
|
-
scopeSnapshotId:
|
|
27349
|
-
scopeHash:
|
|
27350
|
-
baseReferenceSetHash:
|
|
27351
|
-
phase:
|
|
27365
|
+
scopeKey: z121.string().optional(),
|
|
27366
|
+
scopeSnapshotId: z121.string().uuid().optional(),
|
|
27367
|
+
scopeHash: z121.string().optional(),
|
|
27368
|
+
baseReferenceSetHash: z121.string().optional(),
|
|
27369
|
+
phase: z121.enum([
|
|
27352
27370
|
"preparation",
|
|
27353
27371
|
"final"
|
|
27354
27372
|
]).optional(),
|
|
27355
|
-
selectedRefKeys:
|
|
27356
|
-
allowedDecisions:
|
|
27357
|
-
previews:
|
|
27358
|
-
refKey:
|
|
27373
|
+
selectedRefKeys: z121.array(z121.string().min(1).max(200)).optional(),
|
|
27374
|
+
allowedDecisions: z121.array(ProductReferenceResolutionDecisionEnum).optional(),
|
|
27375
|
+
previews: z121.array(z121.object({
|
|
27376
|
+
refKey: z121.string().min(1).max(200)
|
|
27359
27377
|
}).passthrough()).optional(),
|
|
27360
|
-
customAngleDraft:
|
|
27361
|
-
painPointsDraft:
|
|
27362
|
-
benefitsDraft:
|
|
27363
|
-
featuresDraft:
|
|
27364
|
-
angleOptions:
|
|
27365
|
-
index:
|
|
27366
|
-
angle:
|
|
27378
|
+
customAngleDraft: z121.string().optional(),
|
|
27379
|
+
painPointsDraft: z121.string().optional(),
|
|
27380
|
+
benefitsDraft: z121.string().optional(),
|
|
27381
|
+
featuresDraft: z121.array(z121.string()).optional(),
|
|
27382
|
+
angleOptions: z121.array(z121.object({
|
|
27383
|
+
index: z121.number().int().nonnegative(),
|
|
27384
|
+
angle: z121.string().min(1)
|
|
27367
27385
|
}).passthrough()).min(1).max(5).optional(),
|
|
27368
27386
|
// Recommendation consumed only when the caller explicitly sends
|
|
27369
27387
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
@@ -27373,17 +27391,17 @@ var StatusForResolveSchema = z120.object({
|
|
|
27373
27391
|
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
27374
27392
|
// dropped here, so the approval handler below could not echo it back on the
|
|
27375
27393
|
// PATCH — the selection looked accepted and reached no render.
|
|
27376
|
-
angles:
|
|
27377
|
-
id:
|
|
27378
|
-
content:
|
|
27379
|
-
creativeDirection:
|
|
27380
|
-
actor:
|
|
27381
|
-
actorId:
|
|
27394
|
+
angles: z121.array(z121.object({
|
|
27395
|
+
id: z121.string().uuid(),
|
|
27396
|
+
content: z121.string(),
|
|
27397
|
+
creativeDirection: z121.string(),
|
|
27398
|
+
actor: z121.string(),
|
|
27399
|
+
actorId: z121.string().nullable().optional()
|
|
27382
27400
|
}).passthrough()).optional(),
|
|
27383
|
-
panels:
|
|
27384
|
-
beatId:
|
|
27385
|
-
voiceover:
|
|
27386
|
-
visualDirection:
|
|
27401
|
+
panels: z121.array(z121.object({
|
|
27402
|
+
beatId: z121.string(),
|
|
27403
|
+
voiceover: z121.string(),
|
|
27404
|
+
visualDirection: z121.string()
|
|
27387
27405
|
}).passthrough()).optional()
|
|
27388
27406
|
}).optional()
|
|
27389
27407
|
}).passthrough();
|
|
@@ -27736,8 +27754,8 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
27736
27754
|
server.registerTool("verify_landing_commerce", {
|
|
27737
27755
|
description: getProductMakerCapabilityModelGuidance("verify_landing_commerce").summary + " Verifica una correcci\xF3n manual de precios, tachados o packs de una landing editable existente. Solo relee Shopify y los recibos externos; no cambia precios, inventario, SKU ni compra una edici\xF3n. Pasa taskId y editId de la edici\xF3n ya conocida; nunca inventes IDs ni pidas repetir el cambio. Devuelve el estado can\xF3nico por proveedor: conserva el aviso pendiente hasta confirmar los valores reales. Solo despu\xE9s de confirmar todos los valores, el servidor puede volver a habilitar la compra. Web guardada no demuestra publicaci\xF3n p\xFAblica ni checkout; muestra las correcciones y enlaces pendientes sin c\xF3digos t\xE9cnicos. La disponibilidad del editor conserva sus permisos actuales.",
|
|
27738
27756
|
inputSchema: {
|
|
27739
|
-
taskId:
|
|
27740
|
-
editId:
|
|
27757
|
+
taskId: z121.string().uuid(),
|
|
27758
|
+
editId: z121.string().uuid()
|
|
27741
27759
|
},
|
|
27742
27760
|
annotations: OVERWRITES_EXTERNAL
|
|
27743
27761
|
}, async (input) => {
|
|
@@ -27928,16 +27946,16 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
27928
27946
|
if (input.search) qs.set("search", input.search);
|
|
27929
27947
|
const path3 = `/v1/tasks${qs.toString() ? "?" + qs.toString() : ""}`;
|
|
27930
27948
|
const r = await api.getJson(path3);
|
|
27931
|
-
const page =
|
|
27932
|
-
items:
|
|
27949
|
+
const page = z121.object({
|
|
27950
|
+
items: z121.array(z121.unknown())
|
|
27933
27951
|
}).passthrough().safeParse(r);
|
|
27934
27952
|
if (!page.success) return asText(r);
|
|
27935
27953
|
return asText({
|
|
27936
27954
|
...page.data,
|
|
27937
27955
|
items: page.data.items.map((item) => {
|
|
27938
|
-
const servicePage =
|
|
27939
|
-
taskId:
|
|
27940
|
-
resourceType:
|
|
27956
|
+
const servicePage = z121.object({
|
|
27957
|
+
taskId: z121.string().uuid(),
|
|
27958
|
+
resourceType: z121.literal("service_page")
|
|
27941
27959
|
}).passthrough().safeParse(item);
|
|
27942
27960
|
if (!servicePage.success) return item;
|
|
27943
27961
|
return {
|
|
@@ -28259,13 +28277,34 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
28259
28277
|
if (isVideoTask) {
|
|
28260
28278
|
body.selectedAngleIndices = selectedAngleIndices;
|
|
28261
28279
|
const r2 = await api.postJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/approve-video-angles`, body);
|
|
28280
|
+
const angleKeys = r2.approvedAngleKeys;
|
|
28281
|
+
const hasCanonicalKeys = r2.success === true && Array.isArray(angleKeys) && angleKeys.length === selectedAngleIndices.length && angleKeys.length > 0 && angleKeys.every((key) => typeof key === "string" && /^angle_\d+$/.test(key)) && new Set(angleKeys).size === angleKeys.length;
|
|
28262
28282
|
return asText({
|
|
28263
28283
|
...r2,
|
|
28264
28284
|
...status.pauseForVideoScriptReview === true ? {
|
|
28265
|
-
reviewNextAction: {
|
|
28285
|
+
reviewNextAction: hasCanonicalKeys ? {
|
|
28266
28286
|
kind: "poll_video_script",
|
|
28267
28287
|
tool: "get_video_script",
|
|
28268
|
-
|
|
28288
|
+
arguments: {
|
|
28289
|
+
taskId: input.taskId,
|
|
28290
|
+
angleKey: angleKeys[0]
|
|
28291
|
+
},
|
|
28292
|
+
calls: angleKeys.map((angleKey) => ({
|
|
28293
|
+
tool: "get_video_script",
|
|
28294
|
+
arguments: {
|
|
28295
|
+
taskId: input.taskId,
|
|
28296
|
+
angleKey
|
|
28297
|
+
}
|
|
28298
|
+
})),
|
|
28299
|
+
instruction: "Usa exactamente los arguments de cada llamada; no omitas ni derives angleKey del n\xFAmero visible. Consulta cada guion hasta ready/error/approved/skipped. Si devuelve ready, presenta el guion y espera al usuario. Si devuelve error, consulta get_task_status antes de ofrecer una recuperaci\xF3n. Si devuelve approved o skipped, contin\xFAa con la siguiente clave; al terminar, reanuda get_task_status."
|
|
28300
|
+
} : {
|
|
28301
|
+
kind: "poll_task_status",
|
|
28302
|
+
tool: "get_task_status",
|
|
28303
|
+
arguments: {
|
|
28304
|
+
taskId: input.taskId,
|
|
28305
|
+
waitSeconds: 45
|
|
28306
|
+
},
|
|
28307
|
+
instruction: "La aprobaci\xF3n no confirm\xF3 las claves de los guiones programados. Consulta get_task_status para recuperar el estado y su nextAction; no asumas angle_0 ni inicies otra generaci\xF3n."
|
|
28269
28308
|
}
|
|
28270
28309
|
} : {}
|
|
28271
28310
|
});
|
|
@@ -28406,28 +28445,28 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
28406
28445
|
__name(registerTaskTools, "registerTaskTools");
|
|
28407
28446
|
|
|
28408
28447
|
// src/tools/brands.ts
|
|
28409
|
-
import { z as
|
|
28448
|
+
import { z as z122 } from "zod";
|
|
28410
28449
|
var guidance2 = getProductMakerCapabilityModelGuidance("create_brand");
|
|
28411
28450
|
var BRAND_START_REPLAY_WINDOW_MS = 15 * 60 * 1e3;
|
|
28412
28451
|
var BRAND_NOTES_MAX_CHARS = 2e4;
|
|
28413
28452
|
var BRAND_AMBIGUOUS_REQUEST_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
28414
28453
|
var BRAND_CREATION_REQUEST_LIMIT = 500;
|
|
28415
28454
|
var brandCreationRequests = /* @__PURE__ */ new Map();
|
|
28416
|
-
var CreateBrandInput =
|
|
28417
|
-
action:
|
|
28455
|
+
var CreateBrandInput = z122.object({
|
|
28456
|
+
action: z122.enum([
|
|
28418
28457
|
"start",
|
|
28419
28458
|
"status",
|
|
28420
28459
|
"list"
|
|
28421
28460
|
]).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."),
|
|
28422
|
-
sourceUrl:
|
|
28423
|
-
businessNotes:
|
|
28424
|
-
language:
|
|
28461
|
+
sourceUrl: z122.string().trim().min(1).max(BRAND_SOURCE_URL_MAX_LENGTH).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."),
|
|
28462
|
+
businessNotes: z122.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."),
|
|
28463
|
+
language: z122.enum([
|
|
28425
28464
|
"es",
|
|
28426
28465
|
"en",
|
|
28427
28466
|
"pt"
|
|
28428
28467
|
]).optional().describe("Idioma de la conversaci\xF3n. Solo se usa si la p\xE1gina del negocio no deja claro su idioma."),
|
|
28429
|
-
confirmed:
|
|
28430
|
-
brandId:
|
|
28468
|
+
confirmed: z122.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio y que el usuario acepte. Sin confirmed, start solo cotiza y no cobra."),
|
|
28469
|
+
brandId: z122.string().uuid().optional().describe("ID de la marca. Obligatorio para status; no lo inventes.")
|
|
28431
28470
|
}).strict();
|
|
28432
28471
|
function normalizeBrandSourceUrl(raw) {
|
|
28433
28472
|
const value = raw.trim();
|
|
@@ -28710,27 +28749,26 @@ function registerBrandTools(server, api, options = {}) {
|
|
|
28710
28749
|
__name(registerBrandTools, "registerBrandTools");
|
|
28711
28750
|
|
|
28712
28751
|
// src/tools/service-landing.ts
|
|
28713
|
-
import { createHash as createHash3 } from "crypto";
|
|
28714
|
-
import { z as
|
|
28752
|
+
import { createHash as createHash3, randomUUID } from "crypto";
|
|
28753
|
+
import { z as z123 } from "zod";
|
|
28715
28754
|
var guidance3 = getProductMakerCapabilityModelGuidance("create_service_landing");
|
|
28716
28755
|
var SERVICE_LANDING_KEY_NAMESPACE = "6f1d8c2a-3b4e-5f60-8a7b-9c0d1e2f3a4b";
|
|
28717
28756
|
var SERVICE_LANDING_INFO_MAX_CHARS = 2e4;
|
|
28718
|
-
var CreateServiceLandingInput =
|
|
28719
|
-
action:
|
|
28757
|
+
var CreateServiceLandingInput = z123.object({
|
|
28758
|
+
action: z123.enum([
|
|
28720
28759
|
"prepare",
|
|
28721
28760
|
"build",
|
|
28722
28761
|
"status",
|
|
28723
28762
|
"publish"
|
|
28724
|
-
]).default("prepare").describe("prepare (gratis) crea o retoma la landing de la marca y da el precio; build (pagado, confirmed=true)
|
|
28725
|
-
brandId:
|
|
28726
|
-
servicePageId:
|
|
28727
|
-
businessInfo:
|
|
28728
|
-
confirmed:
|
|
28729
|
-
maxCredits:
|
|
28730
|
-
operationId:
|
|
28731
|
-
slug:
|
|
28763
|
+
]).default("prepare").describe("prepare (gratis) crea o retoma la landing de la marca y da el precio; build (pagado, confirmed=true) genera la p\xE1gina directamente con la marca y los datos guardados; status sigue el avance; publish la publica y devuelve su enlace."),
|
|
28764
|
+
brandId: z123.string().uuid().optional().describe("ID de la marca (de create_brand). Obligatorio para prepare. Nunca un taskId de producto."),
|
|
28765
|
+
servicePageId: z123.string().uuid().optional().describe("ID de la landing que devolvi\xF3 prepare. Obligatorio para build, status y publish."),
|
|
28766
|
+
businessInfo: z123.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."),
|
|
28767
|
+
confirmed: z123.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio total y que el usuario acepte. Obligatorio para build."),
|
|
28768
|
+
maxCredits: z123.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."),
|
|
28769
|
+
operationId: z123.string().uuid().optional().describe("Opcional en status: la operaci\xF3n que devolvi\xF3 build."),
|
|
28770
|
+
slug: z123.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.")
|
|
28732
28771
|
}).strict();
|
|
28733
|
-
var SERVICE_LANDING_MAX_BUILD_ATTEMPTS = 10;
|
|
28734
28772
|
function serviceKey(...parts) {
|
|
28735
28773
|
return deriveUuidV5(parts.join(":"), SERVICE_LANDING_KEY_NAMESPACE);
|
|
28736
28774
|
}
|
|
@@ -28780,18 +28818,6 @@ function serviceEditorUrl(api, pageId) {
|
|
|
28780
28818
|
}
|
|
28781
28819
|
}
|
|
28782
28820
|
__name(serviceEditorUrl, "serviceEditorUrl");
|
|
28783
|
-
var MISSING_LABELS = {
|
|
28784
|
-
"business.name": "nombre del negocio",
|
|
28785
|
-
services: "servicios (y su precio, si lo tiene)",
|
|
28786
|
-
"contact.channels": "WhatsApp, tel\xE9fono o enlace para reservar",
|
|
28787
|
-
"contact.primaryCta": "c\xF3mo quiere que lo contacten primero"
|
|
28788
|
-
};
|
|
28789
|
-
function describeMissing(fields) {
|
|
28790
|
-
return [
|
|
28791
|
-
...new Set(fields.map((field) => MISSING_LABELS[field]).filter((label) => Boolean(label)))
|
|
28792
|
-
];
|
|
28793
|
-
}
|
|
28794
|
-
__name(describeMissing, "describeMissing");
|
|
28795
28821
|
function summarizeFacts(facts) {
|
|
28796
28822
|
return {
|
|
28797
28823
|
...facts.business.name ? {
|
|
@@ -28814,14 +28840,13 @@ function summarizeFacts(facts) {
|
|
|
28814
28840
|
}
|
|
28815
28841
|
__name(summarizeFacts, "summarizeFacts");
|
|
28816
28842
|
var OPERATION_ERROR_TEXT = {
|
|
28817
|
-
SERVICE_PAGE_SOURCES_REQUIRED: "No hay informaci\xF3n del negocio para escribir la p\xE1gina. Pide al usuario que cuente sus servicios y
|
|
28818
|
-
SOURCE_TEXT_REQUIRED: "No hay informaci\xF3n del negocio para escribir la p\xE1gina. Pide al usuario que cuente sus servicios y
|
|
28843
|
+
SERVICE_PAGE_SOURCES_REQUIRED: "No hay informaci\xF3n del negocio para escribir la p\xE1gina. Pide al usuario que cuente sus servicios y env\xEDalos como businessInfo al preparar otra landing; muestra su precio y espera autorizaci\xF3n antes de generarla.",
|
|
28844
|
+
SOURCE_TEXT_REQUIRED: "No hay informaci\xF3n del negocio para escribir la p\xE1gina. Pide al usuario que cuente sus servicios y env\xEDalos como businessInfo al preparar otra landing; muestra su precio y espera autorizaci\xF3n antes de generarla."
|
|
28819
28845
|
};
|
|
28820
28846
|
function operationFailureText(code) {
|
|
28821
28847
|
const actionable = code ? OPERATION_ERROR_TEXT[code] : void 0;
|
|
28822
28848
|
if (actionable) return actionable;
|
|
28823
|
-
|
|
28824
|
-
return "La etapa fall\xF3 y no se cobr\xF3. Vuelve a llamar build (mismo maxCredits) sin preguntar: reintenta solo. Si falla 3 veces seguidas, sugiere terminarla en la web de ProductMaker.";
|
|
28849
|
+
return "La etapa termin\xF3 con error y no se cobr\xF3. Explica su causa. Si el usuario quiere otra landing, usa prepare con su marca, muestra el nuevo precio y espera autorizaci\xF3n antes de build; no reintentes la landing fallida autom\xE1ticamente.";
|
|
28825
28850
|
}
|
|
28826
28851
|
__name(operationFailureText, "operationFailureText");
|
|
28827
28852
|
function isPageGenerated(page) {
|
|
@@ -28836,6 +28861,30 @@ function registerServiceLandingTools(server, api) {
|
|
|
28836
28861
|
return pages.find((page) => page.brandId === brandId) ?? null;
|
|
28837
28862
|
}
|
|
28838
28863
|
__name(findPageForBrand, "findPageForBrand");
|
|
28864
|
+
async function createPageForBrand(brandId, includeBrandLogo) {
|
|
28865
|
+
try {
|
|
28866
|
+
const page = await api.postJson("/v1/service-pages", {
|
|
28867
|
+
brandId,
|
|
28868
|
+
includeBrandLogo,
|
|
28869
|
+
// Free workspace identity must not replay a terminal workspace after
|
|
28870
|
+
// its Brand association is released by canonical admission.
|
|
28871
|
+
idempotencyKey: randomUUID()
|
|
28872
|
+
});
|
|
28873
|
+
return {
|
|
28874
|
+
page,
|
|
28875
|
+
resumed: false
|
|
28876
|
+
};
|
|
28877
|
+
} catch (error) {
|
|
28878
|
+
if (error instanceof ApiHttpError && error.status < 500 && serviceApiCode(error) !== "SERVICE_PAGE_CONFLICT_BUSINESS") throw error;
|
|
28879
|
+
const existing = await findPageForBrand(brandId);
|
|
28880
|
+
if (!existing) throw error;
|
|
28881
|
+
return {
|
|
28882
|
+
page: await readPage(existing.id),
|
|
28883
|
+
resumed: true
|
|
28884
|
+
};
|
|
28885
|
+
}
|
|
28886
|
+
}
|
|
28887
|
+
__name(createPageForBrand, "createPageForBrand");
|
|
28839
28888
|
async function addBusinessInfo(page, info) {
|
|
28840
28889
|
try {
|
|
28841
28890
|
await api.postMultipart(`${pagePath(page.id)}/intake`, {}, {
|
|
@@ -28849,24 +28898,39 @@ function registerServiceLandingTools(server, api) {
|
|
|
28849
28898
|
return readPage(page.id);
|
|
28850
28899
|
}
|
|
28851
28900
|
__name(addBusinessInfo, "addBusinessInfo");
|
|
28852
|
-
async function quote(page
|
|
28853
|
-
return api.postJson(`${pagePath(page.id)}/quotes
|
|
28901
|
+
async function quote(page) {
|
|
28902
|
+
return api.postJson(`${pagePath(page.id)}/quotes/generation`, {
|
|
28854
28903
|
expectedRevision: page.draftRevision
|
|
28855
28904
|
});
|
|
28856
28905
|
}
|
|
28857
28906
|
__name(quote, "quote");
|
|
28858
|
-
async function
|
|
28859
|
-
|
|
28860
|
-
|
|
28861
|
-
|
|
28862
|
-
|
|
28863
|
-
|
|
28864
|
-
|
|
28865
|
-
|
|
28866
|
-
|
|
28867
|
-
|
|
28868
|
-
|
|
28869
|
-
|
|
28907
|
+
async function readCreation(pageId) {
|
|
28908
|
+
return ServicePageCreationStatusSchema.parse(await api.getJson(`${pagePath(pageId)}/creation`));
|
|
28909
|
+
}
|
|
28910
|
+
__name(readCreation, "readCreation");
|
|
28911
|
+
function creationResponse(pageId, creation) {
|
|
28912
|
+
const editorUrl = serviceEditorUrl(api, pageId);
|
|
28913
|
+
return asText({
|
|
28914
|
+
servicePageId: pageId,
|
|
28915
|
+
status: creation.status === "running" ? "generating" : "error",
|
|
28916
|
+
...creation.status === "running" && editorUrl ? {
|
|
28917
|
+
editorUrl
|
|
28918
|
+
} : {},
|
|
28919
|
+
...creation.status === "failed" ? {
|
|
28920
|
+
errorCode: creation.errorCode
|
|
28921
|
+
} : {},
|
|
28922
|
+
nextAction: creation.status === "running" ? "La landing se est\xE1 generando. Puedes salir del chat y seguirla en Tareas. Vuelve a llamar action=status con el mismo servicePageId; no inicies otra generaci\xF3n." : "La creaci\xF3n termin\xF3 con error y queda en Tareas. Para crear otra landing, usa action=prepare con la marca elegida y muestra el nuevo precio; solo inicia build cuando el usuario lo autorice. No reutilices esta landing fallida."
|
|
28923
|
+
});
|
|
28924
|
+
}
|
|
28925
|
+
__name(creationResponse, "creationResponse");
|
|
28926
|
+
async function startOperation(page, quoted) {
|
|
28927
|
+
const operation = ServicePageOperationResponseSchema.parse(await api.postJson(`${pagePath(page.id)}/generate`, {
|
|
28928
|
+
quoteId: quoted.quoteId,
|
|
28929
|
+
idempotencyKey: serviceKey(page.id, "generation", page.draftRevision),
|
|
28930
|
+
expectedRevision: page.draftRevision
|
|
28931
|
+
}));
|
|
28932
|
+
if (operation.servicePageId !== page.id || operation.kind !== "generation") {
|
|
28933
|
+
throw mcp(McpErrorCode.INVALID_INPUT, "La operaci\xF3n no pertenece a la generaci\xF3n de esta landing.");
|
|
28870
28934
|
}
|
|
28871
28935
|
return operation;
|
|
28872
28936
|
}
|
|
@@ -28888,7 +28952,7 @@ function registerServiceLandingTools(server, api) {
|
|
|
28888
28952
|
}
|
|
28889
28953
|
__name(generatedResponse, "generatedResponse");
|
|
28890
28954
|
server.registerTool("create_service_landing", {
|
|
28891
|
-
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):
|
|
28955
|
+
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): prepara la landing y devuelve el precio estimado. 2) Pregunta una sola vez los datos que falten; puede omitirlos. 3) Cuando acepte el precio, action=build con servicePageId, confirmed=true, maxCredits y businessInfo: inicia una sola generaci\xF3n con la marca y las fuentes guardadas, sin una lectura pagada previa. 4) action=status hasta generated o error; generando se sigue en Tareas y contin\xFAa aunque cierre el chat. No llames build para continuar una etapa. 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 quiere un video y create_service_images con el mismo brandId si quiere im\xE1genes.",
|
|
28892
28956
|
inputSchema: CreateServiceLandingInput.shape,
|
|
28893
28957
|
annotations: CREATES_PAID
|
|
28894
28958
|
}, async (rawInput) => {
|
|
@@ -28907,37 +28971,37 @@ function registerServiceLandingTools(server, api) {
|
|
|
28907
28971
|
});
|
|
28908
28972
|
}
|
|
28909
28973
|
const existing = await findPageForBrand(input.brandId);
|
|
28910
|
-
let
|
|
28911
|
-
|
|
28912
|
-
|
|
28913
|
-
|
|
28914
|
-
|
|
28974
|
+
let workspace = existing ? {
|
|
28975
|
+
page: await readPage(existing.id),
|
|
28976
|
+
resumed: true
|
|
28977
|
+
} : await createPageForBrand(input.brandId, Boolean(brand.logoKey));
|
|
28978
|
+
let page2 = workspace.page;
|
|
28915
28979
|
if (isPageGenerated(page2)) return generatedResponse(page2);
|
|
28980
|
+
let creation2 = await readCreation(page2.id);
|
|
28981
|
+
if (existing && creation2.status === "failed") {
|
|
28982
|
+
workspace = await createPageForBrand(input.brandId, Boolean(brand.logoKey));
|
|
28983
|
+
page2 = workspace.page;
|
|
28984
|
+
if (isPageGenerated(page2)) return generatedResponse(page2);
|
|
28985
|
+
creation2 = await readCreation(page2.id);
|
|
28986
|
+
}
|
|
28987
|
+
if (creation2.status !== "idle") return creationResponse(page2.id, creation2);
|
|
28916
28988
|
if (input.businessInfo) page2 = await addBusinessInfo(page2, input.businessInfo);
|
|
28917
|
-
const
|
|
28918
|
-
const generationQuote = await quote(page2, "generation");
|
|
28919
|
-
const facts = factsQuote ? quoteRange(factsQuote) : {
|
|
28920
|
-
min: 0,
|
|
28921
|
-
max: 0
|
|
28922
|
-
};
|
|
28989
|
+
const generationQuote = await quote(page2);
|
|
28923
28990
|
const build = quoteRange(generationQuote);
|
|
28924
|
-
const totalMin =
|
|
28925
|
-
const totalCredits =
|
|
28926
|
-
const requiredBalanceCredits =
|
|
28991
|
+
const totalMin = build.min;
|
|
28992
|
+
const totalCredits = build.max;
|
|
28993
|
+
const requiredBalanceCredits = generationQuote.credits;
|
|
28927
28994
|
const askFor = page2.facts.contact.channels.some((channel) => channel.kind === "whatsapp") ? "su ciudad o zona y sus servicios con precio (su WhatsApp ya viene de la marca: no lo pidas)" : "su WhatsApp o tel\xE9fono, su ciudad o zona y sus servicios con precio";
|
|
28928
28995
|
const priceText = totalMin === totalCredits ? `${totalCredits} cr\xE9ditos` : `entre ${totalMin} y ${totalCredits} cr\xE9ditos`;
|
|
28929
28996
|
return asText({
|
|
28930
28997
|
servicePageId: page2.id,
|
|
28931
28998
|
charged: false,
|
|
28932
|
-
...
|
|
28999
|
+
...workspace.resumed ? {
|
|
28933
29000
|
resumed: true
|
|
28934
29001
|
} : {},
|
|
28935
29002
|
business: page2.businessName,
|
|
28936
29003
|
known: summarizeFacts(page2.facts),
|
|
28937
29004
|
price: {
|
|
28938
|
-
...factsQuote ? {
|
|
28939
|
-
readBusinessInfoCredits: facts
|
|
28940
|
-
} : {},
|
|
28941
29005
|
buildPageCredits: build,
|
|
28942
29006
|
totalMinCredits: totalMin,
|
|
28943
29007
|
totalCredits,
|
|
@@ -28950,33 +29014,22 @@ function registerServiceLandingTools(server, api) {
|
|
|
28950
29014
|
throw mcp(McpErrorCode.INVALID_INPUT, "servicePageId es obligatorio: usa el que devolvi\xF3 prepare. No se cobr\xF3 nada.");
|
|
28951
29015
|
}
|
|
28952
29016
|
if (input.action === "status") {
|
|
28953
|
-
|
|
28954
|
-
|
|
28955
|
-
|
|
28956
|
-
|
|
28957
|
-
return asText({
|
|
28958
|
-
servicePageId: input.servicePageId,
|
|
28959
|
-
status: creation.status,
|
|
28960
|
-
...editorUrl ? {
|
|
28961
|
-
editorUrl
|
|
28962
|
-
} : {},
|
|
28963
|
-
...creation.status === "failed" ? {
|
|
28964
|
-
errorCode: creation.errorCode,
|
|
28965
|
-
reason: "No pudimos terminar la p\xE1gina. Tus datos siguen guardados."
|
|
28966
|
-
} : {},
|
|
28967
|
-
nextAction: creation.status === "running" ? "Sigue en curso. Vuelve a llamar action=status con el mismo servicePageId en unos segundos; no inicies otra etapa." : "La creaci\xF3n fall\xF3. Muestra el motivo al usuario y el enlace al editor, si est\xE1 disponible; no inicies otra generaci\xF3n sin su autorizaci\xF3n."
|
|
28968
|
-
});
|
|
28969
|
-
}
|
|
28970
|
-
}
|
|
29017
|
+
const creation2 = await readCreation(input.servicePageId);
|
|
29018
|
+
if (creation2.status !== "idle") return creationResponse(input.servicePageId, creation2);
|
|
29019
|
+
let page2 = await readPage(input.servicePageId);
|
|
29020
|
+
if (isPageGenerated(page2)) return generatedResponse(page2);
|
|
28971
29021
|
let operation2 = null;
|
|
28972
29022
|
if (input.operationId) {
|
|
28973
|
-
operation2 = await api.getJson(`/v1/service-pages/operations/${encodeURIComponent(input.operationId)}`);
|
|
29023
|
+
operation2 = ServicePageOperationResponseSchema.parse(await api.getJson(`/v1/service-pages/operations/${encodeURIComponent(input.operationId)}`));
|
|
29024
|
+
if (operation2.servicePageId !== input.servicePageId) {
|
|
29025
|
+
throw mcp(McpErrorCode.INVALID_INPUT, "La operaci\xF3n no pertenece a esta landing.");
|
|
29026
|
+
}
|
|
28974
29027
|
if (operation2.status === "queued" || operation2.status === "running") {
|
|
28975
29028
|
return asText({
|
|
28976
29029
|
servicePageId: input.servicePageId,
|
|
28977
29030
|
operationId: operation2.opId,
|
|
28978
29031
|
step: operation2.kind === "generation" ? "creando la p\xE1gina" : "leyendo los datos del negocio",
|
|
28979
|
-
status:
|
|
29032
|
+
status: "generating",
|
|
28980
29033
|
nextAction: "Sigue en curso. Llama action=status otra vez con el mismo operationId en unos segundos; no inicies otra etapa."
|
|
28981
29034
|
});
|
|
28982
29035
|
}
|
|
@@ -28984,49 +29037,28 @@ function registerServiceLandingTools(server, api) {
|
|
|
28984
29037
|
return asText({
|
|
28985
29038
|
servicePageId: input.servicePageId,
|
|
28986
29039
|
operationId: operation2.opId,
|
|
28987
|
-
status: "
|
|
29040
|
+
status: "generating",
|
|
28988
29041
|
// Unknown on purpose: the charge is exactly what is being
|
|
28989
29042
|
// confirmed. Settlement only charges a completed step.
|
|
28990
|
-
nextAction: "
|
|
29043
|
+
nextAction: "La landing se sigue generando: el servidor est\xE1 confirmando si se complet\xF3 y la resuelve solo. Si no se completa, se devuelve lo cobrado. Dile al usuario que sus datos est\xE1n guardados; vuelve a llamar action=status con el mismo operationId en unos minutos. No inicies otra generaci\xF3n mientras tanto."
|
|
28991
29044
|
});
|
|
28992
29045
|
}
|
|
28993
29046
|
if (operation2.status !== "succeeded") {
|
|
28994
29047
|
return asText({
|
|
28995
29048
|
servicePageId: input.servicePageId,
|
|
28996
29049
|
operationId: operation2.opId,
|
|
28997
|
-
status: "
|
|
29050
|
+
status: "error",
|
|
29051
|
+
errorCode: operation2.errorCode,
|
|
28998
29052
|
charged: false,
|
|
28999
29053
|
reason: operationFailureText(operation2.errorCode)
|
|
29000
29054
|
});
|
|
29001
29055
|
}
|
|
29002
29056
|
}
|
|
29003
|
-
|
|
29057
|
+
if (operation2?.status === "succeeded") page2 = await readPage(input.servicePageId);
|
|
29004
29058
|
if (isPageGenerated(page2)) return generatedResponse(page2);
|
|
29005
|
-
|
|
29006
|
-
|
|
29007
|
-
|
|
29008
|
-
servicePageId: page2.id,
|
|
29009
|
-
status: "not_generated",
|
|
29010
|
-
operationStatus: "unknown",
|
|
29011
|
-
...editorUrl ? {
|
|
29012
|
-
editorUrl
|
|
29013
|
-
} : {},
|
|
29014
|
-
reason: "La p\xE1gina a\xFAn no tiene un resultado generado. Sin operationId no podemos determinar el estado de la operaci\xF3n; consulta sus tareas o abre el editor para revisar su progreso.",
|
|
29015
|
-
nextAction: {
|
|
29016
|
-
tool: "list_tasks",
|
|
29017
|
-
arguments: {}
|
|
29018
|
-
}
|
|
29019
|
-
});
|
|
29020
|
-
}
|
|
29021
|
-
const missing = describeMissing(page2.missingFields);
|
|
29022
|
-
return asText({
|
|
29023
|
-
servicePageId: page2.id,
|
|
29024
|
-
status: page2.factExtractionCompleted ? "business_info_read" : "waiting_for_build",
|
|
29025
|
-
found: summarizeFacts(page2.facts),
|
|
29026
|
-
...missing.length ? {
|
|
29027
|
-
stillMissing: missing
|
|
29028
|
-
} : {},
|
|
29029
|
-
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."
|
|
29059
|
+
return creationResponse(page2.id, {
|
|
29060
|
+
status: "failed",
|
|
29061
|
+
errorCode: "SERVICE_PAGE_CREATION_STALLED"
|
|
29030
29062
|
});
|
|
29031
29063
|
}
|
|
29032
29064
|
if (input.action === "publish") {
|
|
@@ -29044,7 +29076,7 @@ function registerServiceLandingTools(server, api) {
|
|
|
29044
29076
|
});
|
|
29045
29077
|
return asText({
|
|
29046
29078
|
servicePageId: page2.id,
|
|
29047
|
-
status: "
|
|
29079
|
+
status: "generated",
|
|
29048
29080
|
publicUrl: published.publicUrl,
|
|
29049
29081
|
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)"}.`
|
|
29050
29082
|
});
|
|
@@ -29052,7 +29084,8 @@ function registerServiceLandingTools(server, api) {
|
|
|
29052
29084
|
if (serviceApiCode(error)?.includes("SLUG")) {
|
|
29053
29085
|
return asText({
|
|
29054
29086
|
servicePageId: page2.id,
|
|
29055
|
-
status: "
|
|
29087
|
+
status: "generated",
|
|
29088
|
+
publicationErrorCode: "SERVICE_PAGE_SLUG_TAKEN",
|
|
29056
29089
|
slug,
|
|
29057
29090
|
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.`
|
|
29058
29091
|
});
|
|
@@ -29065,12 +29098,10 @@ function registerServiceLandingTools(server, api) {
|
|
|
29065
29098
|
}
|
|
29066
29099
|
let page = await readPage(input.servicePageId);
|
|
29067
29100
|
if (isPageGenerated(page)) return generatedResponse(page);
|
|
29068
|
-
const
|
|
29101
|
+
const creation = await readCreation(page.id);
|
|
29102
|
+
if (creation.status !== "idle") return creationResponse(page.id, creation);
|
|
29069
29103
|
if (input.businessInfo) page = await addBusinessInfo(page, input.businessInfo);
|
|
29070
|
-
const
|
|
29071
|
-
const needsFacts = !page.factExtractionCompleted || addedInfo;
|
|
29072
|
-
const purpose = needsFacts ? "fact-extraction" : "generation";
|
|
29073
|
-
const quoted = await quote(page, purpose);
|
|
29104
|
+
const quoted = await quote(page);
|
|
29074
29105
|
const credits = quoteRange(quoted).max;
|
|
29075
29106
|
if (credits > input.maxCredits) {
|
|
29076
29107
|
return asText({
|
|
@@ -29080,15 +29111,18 @@ function registerServiceLandingTools(server, api) {
|
|
|
29080
29111
|
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}.`
|
|
29081
29112
|
});
|
|
29082
29113
|
}
|
|
29083
|
-
await requirePaidCreativeConfirmation(server,
|
|
29084
|
-
const operation = await startOperation(page,
|
|
29114
|
+
await requirePaidCreativeConfirmation(server, `Generar la landing del negocio. Precio estimado: hasta ${credits} cr\xE9ditos (total aceptado: ${input.maxCredits}).`);
|
|
29115
|
+
const operation = await startOperation(page, quoted);
|
|
29085
29116
|
return asText({
|
|
29086
29117
|
servicePageId: page.id,
|
|
29087
29118
|
operationId: operation.opId,
|
|
29088
|
-
step:
|
|
29089
|
-
status: operation.status,
|
|
29119
|
+
step: "creando la p\xE1gina",
|
|
29120
|
+
status: operation.status === "failed" || operation.status === "conflict" ? "error" : "generating",
|
|
29121
|
+
...operation.errorCode ? {
|
|
29122
|
+
errorCode: operation.errorCode
|
|
29123
|
+
} : {},
|
|
29090
29124
|
priceCredits: credits,
|
|
29091
|
-
nextAction: operation.status === "failed" ? operationFailureText(operation.errorCode) :
|
|
29125
|
+
nextAction: operation.status === "failed" || operation.status === "conflict" ? operationFailureText(operation.errorCode) : "La landing se est\xE1 generando y aparece en Tareas. Llama action=status con servicePageId hasta generated o error. La generaci\xF3n contin\xFAa aunque salgas del chat."
|
|
29092
29126
|
});
|
|
29093
29127
|
} catch (error) {
|
|
29094
29128
|
throw translateError(error);
|
|
@@ -29098,7 +29132,7 @@ function registerServiceLandingTools(server, api) {
|
|
|
29098
29132
|
__name(registerServiceLandingTools, "registerServiceLandingTools");
|
|
29099
29133
|
|
|
29100
29134
|
// src/tools/service-images.ts
|
|
29101
|
-
import { z as
|
|
29135
|
+
import { z as z124 } from "zod";
|
|
29102
29136
|
var guidance4 = getProductMakerCapabilityModelGuidance("create_service_images");
|
|
29103
29137
|
var SERVICE_IMAGE_FORMATS = [
|
|
29104
29138
|
"4:5",
|
|
@@ -29111,23 +29145,23 @@ var LANGUAGE_TAGS = {
|
|
|
29111
29145
|
en: "en-US",
|
|
29112
29146
|
pt: "pt-BR"
|
|
29113
29147
|
};
|
|
29114
|
-
var CreateServiceImagesInput =
|
|
29115
|
-
brandId:
|
|
29116
|
-
instruction:
|
|
29117
|
-
delegateToAi:
|
|
29118
|
-
format:
|
|
29119
|
-
count:
|
|
29120
|
-
quality:
|
|
29148
|
+
var CreateServiceImagesInput = z124.object({
|
|
29149
|
+
brandId: z124.string().uuid().optional().describe("ID de la marca (de create_brand). Recomendado: da nombre, colores, tono y servicios. Nunca un taskId de producto."),
|
|
29150
|
+
instruction: z124.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."),
|
|
29151
|
+
delegateToAi: z124.boolean().optional().describe("true si el usuario prefiere que la IA elija qu\xE9 mostrar a partir de su marca."),
|
|
29152
|
+
format: z124.enum(SERVICE_IMAGE_FORMATS).optional().describe('Formato: "4:5" (feed, por defecto), "9:16" (historias/reels) o "1:1" (cuadrado).'),
|
|
29153
|
+
count: z124.number().int().min(1).max(SERVICE_IMAGE_MAX_COUNT).optional().describe("Cu\xE1ntas im\xE1genes, de 1 a 3 (por defecto 1)."),
|
|
29154
|
+
quality: z124.enum([
|
|
29121
29155
|
"standard",
|
|
29122
29156
|
"high"
|
|
29123
29157
|
]).optional().describe('Calidad: "standard" (por defecto) o "high" (m\xE1s cara; no disponible en 4:5).'),
|
|
29124
|
-
language:
|
|
29158
|
+
language: z124.enum([
|
|
29125
29159
|
"es",
|
|
29126
29160
|
"en",
|
|
29127
29161
|
"pt"
|
|
29128
29162
|
]).optional().describe("Idioma de los textos de la imagen (por defecto es)."),
|
|
29129
|
-
adCopy:
|
|
29130
|
-
confirmed:
|
|
29163
|
+
adCopy: z124.boolean().optional().describe("Compatibilidad con clientes anteriores: el copy para publicar siempre est\xE1 incluido en cada imagen de servicios."),
|
|
29164
|
+
confirmed: z124.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio y que el usuario acepte. Sin confirmed, solo cotiza y no cobra.")
|
|
29131
29165
|
}).strict();
|
|
29132
29166
|
function serviceImageModel(quality) {
|
|
29133
29167
|
return quality === "high" ? AD_CREATIVE_MODELS.PREMIUM : AD_CREATIVE_MODELS.STANDARD;
|
|
@@ -29238,7 +29272,7 @@ function registerServiceImageTools(server, api, options = {}) {
|
|
|
29238
29272
|
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."
|
|
29239
29273
|
});
|
|
29240
29274
|
} catch (error) {
|
|
29241
|
-
if (error instanceof
|
|
29275
|
+
if (error instanceof z124.ZodError) throw invalidInputFromZod(error);
|
|
29242
29276
|
throw translateError(error);
|
|
29243
29277
|
}
|
|
29244
29278
|
});
|
|
@@ -29246,7 +29280,7 @@ function registerServiceImageTools(server, api, options = {}) {
|
|
|
29246
29280
|
__name(registerServiceImageTools, "registerServiceImageTools");
|
|
29247
29281
|
|
|
29248
29282
|
// src/tools/creatives.ts
|
|
29249
|
-
import { z as
|
|
29283
|
+
import { z as z125 } from "zod";
|
|
29250
29284
|
var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
|
|
29251
29285
|
var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
|
|
29252
29286
|
var generateImageCreativesGuidance = getProductMakerCapabilityModelGuidance("generate_image_creatives");
|
|
@@ -29257,72 +29291,72 @@ var CreativeRefs = {
|
|
|
29257
29291
|
images: ImagesInput,
|
|
29258
29292
|
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.'),
|
|
29259
29293
|
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.'),
|
|
29260
|
-
price:
|
|
29261
|
-
offer:
|
|
29262
|
-
compareAtPrice:
|
|
29263
|
-
productContext:
|
|
29264
|
-
productLabel:
|
|
29265
|
-
buyerPersona:
|
|
29266
|
-
isCombo:
|
|
29267
|
-
competitorScanEnabled:
|
|
29268
|
-
brandId:
|
|
29294
|
+
price: z125.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.'),
|
|
29295
|
+
offer: z125.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.'),
|
|
29296
|
+
compareAtPrice: z125.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
|
|
29297
|
+
productContext: z125.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."),
|
|
29298
|
+
productLabel: z125.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.'),
|
|
29299
|
+
buyerPersona: z125.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.'),
|
|
29300
|
+
isCombo: z125.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
29301
|
+
competitorScanEnabled: z125.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."),
|
|
29302
|
+
brandId: z125.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.")
|
|
29269
29303
|
};
|
|
29270
|
-
var VideoPresenter =
|
|
29271
|
-
|
|
29272
|
-
mode:
|
|
29304
|
+
var VideoPresenter = z125.discriminatedUnion("mode", [
|
|
29305
|
+
z125.object({
|
|
29306
|
+
mode: z125.literal("none")
|
|
29273
29307
|
}).strict(),
|
|
29274
|
-
|
|
29275
|
-
mode:
|
|
29276
|
-
actorId:
|
|
29308
|
+
z125.object({
|
|
29309
|
+
mode: z125.literal("saved_actor"),
|
|
29310
|
+
actorId: z125.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
|
|
29277
29311
|
}).strict(),
|
|
29278
|
-
|
|
29279
|
-
mode:
|
|
29280
|
-
description:
|
|
29312
|
+
z125.object({
|
|
29313
|
+
mode: z125.literal("described"),
|
|
29314
|
+
description: z125.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
|
|
29281
29315
|
}).strict()
|
|
29282
29316
|
]);
|
|
29283
|
-
var VideoCreativeSetup =
|
|
29284
|
-
|
|
29285
|
-
mode:
|
|
29317
|
+
var VideoCreativeSetup = z125.discriminatedUnion("mode", [
|
|
29318
|
+
z125.object({
|
|
29319
|
+
mode: z125.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
|
|
29286
29320
|
}).strict(),
|
|
29287
|
-
|
|
29288
|
-
mode:
|
|
29321
|
+
z125.object({
|
|
29322
|
+
mode: z125.literal("guided"),
|
|
29289
29323
|
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.'),
|
|
29290
|
-
buyerPersona:
|
|
29324
|
+
buyerPersona: z125.string().trim().min(1).max(360),
|
|
29291
29325
|
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.'),
|
|
29292
29326
|
presenter: VideoPresenter,
|
|
29293
|
-
creativeDirection:
|
|
29327
|
+
creativeDirection: z125.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
|
|
29294
29328
|
}).strict()
|
|
29295
29329
|
]);
|
|
29296
|
-
var VideoInput =
|
|
29330
|
+
var VideoInput = z125.object({
|
|
29297
29331
|
images: CreativeRefs.images.optional(),
|
|
29298
29332
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
29299
|
-
title:
|
|
29300
|
-
primaryAngle:
|
|
29333
|
+
title: z125.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
29334
|
+
primaryAngle: z125.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".'),
|
|
29301
29335
|
language: CreativeRefs.language,
|
|
29302
29336
|
country: CreativeRefs.country,
|
|
29303
|
-
aspectRatio:
|
|
29337
|
+
aspectRatio: z125.enum([
|
|
29304
29338
|
"9:16",
|
|
29305
29339
|
"16:9",
|
|
29306
29340
|
"Auto"
|
|
29307
29341
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
29308
29342
|
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.'),
|
|
29309
|
-
durationSeconds:
|
|
29310
|
-
videoAngleCount:
|
|
29343
|
+
durationSeconds: z125.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."),
|
|
29344
|
+
videoAngleCount: z125.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."),
|
|
29311
29345
|
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.'),
|
|
29312
29346
|
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."),
|
|
29313
29347
|
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.'),
|
|
29314
29348
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
|
|
29315
29349
|
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.'),
|
|
29316
|
-
features:
|
|
29350
|
+
features: z125.array(z125.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
|
|
29317
29351
|
price: CreativeRefs.price,
|
|
29318
29352
|
offer: CreativeRefs.offer,
|
|
29319
29353
|
productContext: CreativeRefs.productContext,
|
|
29320
29354
|
productLabel: CreativeRefs.productLabel,
|
|
29321
|
-
cta:
|
|
29322
|
-
scriptNotes:
|
|
29355
|
+
cta: z125.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.'),
|
|
29356
|
+
scriptNotes: z125.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.'),
|
|
29323
29357
|
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.'),
|
|
29324
29358
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
29325
|
-
replicateWinnerWhenFound:
|
|
29359
|
+
replicateWinnerWhenFound: z125.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."),
|
|
29326
29360
|
brandId: CreativeRefs.brandId
|
|
29327
29361
|
}).strict();
|
|
29328
29362
|
var FreeVideoQualityEnum = VideoQualityEnum;
|
|
@@ -29340,7 +29374,7 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
29340
29374
|
length: 29
|
|
29341
29375
|
}, (_, index) => index + 2)
|
|
29342
29376
|
};
|
|
29343
|
-
var FreeVideoModelIdEnum =
|
|
29377
|
+
var FreeVideoModelIdEnum = z125.enum([
|
|
29344
29378
|
"grok-imagine-video-1-5-preview",
|
|
29345
29379
|
"minimax-h3/reference-to-video",
|
|
29346
29380
|
"wan/3-0-video",
|
|
@@ -29351,66 +29385,66 @@ var FREE_SELECTABLE_MODEL_IDS = [
|
|
|
29351
29385
|
"minimax-h3/reference-to-video",
|
|
29352
29386
|
"wan/3-0-video"
|
|
29353
29387
|
];
|
|
29354
|
-
var FreeVideoInput =
|
|
29388
|
+
var FreeVideoInput = z125.object({
|
|
29355
29389
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
29356
|
-
rawPrompt:
|
|
29390
|
+
rawPrompt: z125.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."),
|
|
29357
29391
|
language: CreativeRefs.language,
|
|
29358
29392
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan).'),
|
|
29359
29393
|
// Retired in MCP 7.0.0 (Task 11): kept in the schema, accepted, and
|
|
29360
29394
|
// parsed ONLY so the handler can reject it with a message naming its
|
|
29361
29395
|
// replacement — dropping it silently would turn a caller's explicit
|
|
29362
29396
|
// Pro++ pick into a quiet fallback to Wan.
|
|
29363
|
-
freeProModel:
|
|
29397
|
+
freeProModel: z125.enum([
|
|
29364
29398
|
"wan",
|
|
29365
29399
|
"seedance"
|
|
29366
29400
|
]).optional().describe("ELIMINADO en MCP 7.0.0 \u2014 usa `modelId`. Enviar este campo produce un error."),
|
|
29367
29401
|
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.'),
|
|
29368
|
-
durationSeconds:
|
|
29402
|
+
durationSeconds: z125.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."),
|
|
29369
29403
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
29370
|
-
generateAiMusic:
|
|
29404
|
+
generateAiMusic: z125.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.'),
|
|
29371
29405
|
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.'),
|
|
29372
|
-
addSubtitles:
|
|
29373
|
-
idempotencyKey:
|
|
29406
|
+
addSubtitles: z125.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
29407
|
+
idempotencyKey: z125.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.")
|
|
29374
29408
|
}).strict();
|
|
29375
29409
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
29376
|
-
var ImageCreativeSetup =
|
|
29377
|
-
|
|
29378
|
-
mode:
|
|
29410
|
+
var ImageCreativeSetup = z125.discriminatedUnion("mode", [
|
|
29411
|
+
z125.object({
|
|
29412
|
+
mode: z125.literal("automatic")
|
|
29379
29413
|
}).strict(),
|
|
29380
|
-
|
|
29381
|
-
mode:
|
|
29382
|
-
creativeArchetype:
|
|
29414
|
+
z125.object({
|
|
29415
|
+
mode: z125.literal("guided"),
|
|
29416
|
+
creativeArchetype: z125.union([
|
|
29383
29417
|
ImageCreativeArchetypeEnum,
|
|
29384
|
-
|
|
29418
|
+
z125.literal("ai_decides")
|
|
29385
29419
|
]),
|
|
29386
|
-
visualTreatment:
|
|
29420
|
+
visualTreatment: z125.union([
|
|
29387
29421
|
ImageCreativeVisualTreatmentEnum,
|
|
29388
|
-
|
|
29422
|
+
z125.literal("ai_decides")
|
|
29389
29423
|
]),
|
|
29390
29424
|
aspectRatio: ImageCreativeAspectRatioEnum,
|
|
29391
|
-
nVariants:
|
|
29392
|
-
|
|
29393
|
-
|
|
29394
|
-
|
|
29395
|
-
|
|
29396
|
-
|
|
29425
|
+
nVariants: z125.union([
|
|
29426
|
+
z125.literal(1),
|
|
29427
|
+
z125.literal(2),
|
|
29428
|
+
z125.literal(3),
|
|
29429
|
+
z125.literal(4),
|
|
29430
|
+
z125.literal(6)
|
|
29397
29431
|
]),
|
|
29398
|
-
imageModel:
|
|
29399
|
-
styleMix:
|
|
29400
|
-
generateAdCopy:
|
|
29401
|
-
competitorScanEnabled:
|
|
29402
|
-
productContext:
|
|
29403
|
-
creativeDirection:
|
|
29404
|
-
brandColors:
|
|
29405
|
-
headline:
|
|
29406
|
-
subheadline:
|
|
29432
|
+
imageModel: z125.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.'),
|
|
29433
|
+
styleMix: z125.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
|
|
29434
|
+
generateAdCopy: z125.boolean(),
|
|
29435
|
+
competitorScanEnabled: z125.boolean(),
|
|
29436
|
+
productContext: z125.string().trim().min(1).max(3e3).nullable(),
|
|
29437
|
+
creativeDirection: z125.string().trim().min(1).max(500).nullable(),
|
|
29438
|
+
brandColors: z125.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.'),
|
|
29439
|
+
headline: z125.string().trim().min(1).max(120).nullable(),
|
|
29440
|
+
subheadline: z125.string().trim().min(1).max(160).nullable()
|
|
29407
29441
|
}).strict()
|
|
29408
29442
|
]).superRefine((setup, context) => {
|
|
29409
29443
|
if (setup.mode !== "guided") return;
|
|
29410
29444
|
const needsStyleMix = setup.nVariants >= 2 && setup.nVariants <= 4;
|
|
29411
29445
|
if (needsStyleMix && setup.styleMix === null) {
|
|
29412
29446
|
context.addIssue({
|
|
29413
|
-
code:
|
|
29447
|
+
code: z125.ZodIssueCode.custom,
|
|
29414
29448
|
path: [
|
|
29415
29449
|
"styleMix"
|
|
29416
29450
|
],
|
|
@@ -29419,7 +29453,7 @@ var ImageCreativeSetup = z124.discriminatedUnion("mode", [
|
|
|
29419
29453
|
}
|
|
29420
29454
|
if (!needsStyleMix && setup.styleMix !== null) {
|
|
29421
29455
|
context.addIssue({
|
|
29422
|
-
code:
|
|
29456
|
+
code: z125.ZodIssueCode.custom,
|
|
29423
29457
|
path: [
|
|
29424
29458
|
"styleMix"
|
|
29425
29459
|
],
|
|
@@ -29428,7 +29462,7 @@ var ImageCreativeSetup = z124.discriminatedUnion("mode", [
|
|
|
29428
29462
|
}
|
|
29429
29463
|
if (imageResolutionOf(setup.imageModel) === "2k" && setup.aspectRatio === "4:5") {
|
|
29430
29464
|
context.addIssue({
|
|
29431
|
-
code:
|
|
29465
|
+
code: z125.ZodIssueCode.custom,
|
|
29432
29466
|
path: [
|
|
29433
29467
|
"aspectRatio"
|
|
29434
29468
|
],
|
|
@@ -29436,55 +29470,55 @@ var ImageCreativeSetup = z124.discriminatedUnion("mode", [
|
|
|
29436
29470
|
});
|
|
29437
29471
|
}
|
|
29438
29472
|
});
|
|
29439
|
-
var LandingCreativeSetup =
|
|
29440
|
-
|
|
29441
|
-
mode:
|
|
29473
|
+
var LandingCreativeSetup = z125.discriminatedUnion("mode", [
|
|
29474
|
+
z125.object({
|
|
29475
|
+
mode: z125.literal("automatic")
|
|
29442
29476
|
}).strict(),
|
|
29443
|
-
|
|
29444
|
-
mode:
|
|
29477
|
+
z125.object({
|
|
29478
|
+
mode: z125.literal("guided"),
|
|
29445
29479
|
landingStrategy: LandingStrategyEnum,
|
|
29446
29480
|
landingImageFormat: LandingImageFormatEnum,
|
|
29447
|
-
competitorScanEnabled:
|
|
29448
|
-
paymentMethods:
|
|
29449
|
-
productContext:
|
|
29450
|
-
creativeDirection:
|
|
29451
|
-
headline:
|
|
29452
|
-
subheadline:
|
|
29481
|
+
competitorScanEnabled: z125.boolean(),
|
|
29482
|
+
paymentMethods: z125.string().trim().min(1).max(240).nullable(),
|
|
29483
|
+
productContext: z125.string().trim().min(1).max(3e3).nullable(),
|
|
29484
|
+
creativeDirection: z125.string().trim().min(1).max(500).nullable(),
|
|
29485
|
+
headline: z125.string().trim().min(1).max(120).nullable(),
|
|
29486
|
+
subheadline: z125.string().trim().min(1).max(160).nullable()
|
|
29453
29487
|
}).strict()
|
|
29454
29488
|
]);
|
|
29455
|
-
var ImageInput =
|
|
29489
|
+
var ImageInput = z125.object({
|
|
29456
29490
|
images: CreativeRefs.images.optional(),
|
|
29457
29491
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
29458
|
-
productTitle:
|
|
29459
|
-
primaryAngle:
|
|
29492
|
+
productTitle: z125.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
29493
|
+
primaryAngle: z125.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.'),
|
|
29460
29494
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
29461
29495
|
language: CreativeRefs.language,
|
|
29462
29496
|
country: CreativeRefs.country,
|
|
29463
29497
|
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."),
|
|
29464
29498
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
29465
|
-
features:
|
|
29499
|
+
features: z125.array(z125.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
29466
29500
|
price: CreativeRefs.price,
|
|
29467
|
-
omitPrice:
|
|
29501
|
+
omitPrice: z125.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."),
|
|
29468
29502
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
29469
29503
|
offer: CreativeRefs.offer,
|
|
29470
29504
|
productLabel: CreativeRefs.productLabel,
|
|
29471
29505
|
isCombo: CreativeRefs.isCombo,
|
|
29472
29506
|
brandId: CreativeRefs.brandId
|
|
29473
29507
|
});
|
|
29474
|
-
var EditImageCreativeInput =
|
|
29475
|
-
taskId:
|
|
29476
|
-
variantIndex:
|
|
29477
|
-
clientRequestId:
|
|
29478
|
-
instruction:
|
|
29508
|
+
var EditImageCreativeInput = z125.object({
|
|
29509
|
+
taskId: z125.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
|
|
29510
|
+
variantIndex: z125.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
|
|
29511
|
+
clientRequestId: z125.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."),
|
|
29512
|
+
instruction: z125.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".')
|
|
29479
29513
|
});
|
|
29480
|
-
var LandingInput =
|
|
29481
|
-
includeInitialLandingGif:
|
|
29514
|
+
var LandingInput = z125.object({
|
|
29515
|
+
includeInitialLandingGif: z125.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."),
|
|
29482
29516
|
images: CreativeRefs.images.optional(),
|
|
29483
29517
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
29484
29518
|
language: CreativeRefs.language,
|
|
29485
29519
|
country: CreativeRefs.country,
|
|
29486
29520
|
creativeSetup: LandingCreativeSetup.describe("Configuraci\xF3n obligatoria de landing cl\xE1sica equivalente al wizard web. No expone landing editable/code mientras siga siendo admin-only."),
|
|
29487
|
-
salesAngle:
|
|
29521
|
+
salesAngle: z125.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.'),
|
|
29488
29522
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
29489
29523
|
price: CreativeRefs.price,
|
|
29490
29524
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -29766,8 +29800,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
29766
29800
|
server.registerTool("get_image_edit_status", {
|
|
29767
29801
|
description: imageEditStatusGuidance.summary,
|
|
29768
29802
|
inputSchema: {
|
|
29769
|
-
taskId:
|
|
29770
|
-
operationId:
|
|
29803
|
+
taskId: z125.string().uuid(),
|
|
29804
|
+
operationId: z125.string().uuid()
|
|
29771
29805
|
},
|
|
29772
29806
|
annotations: READS_INTERNAL
|
|
29773
29807
|
}, async (input) => {
|
|
@@ -29824,10 +29858,10 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
29824
29858
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
29825
29859
|
|
|
29826
29860
|
// src/tools/creative-variants.ts
|
|
29827
|
-
import { z as
|
|
29861
|
+
import { z as z126 } from "zod";
|
|
29828
29862
|
var estimateCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("estimate_creative_variant");
|
|
29829
29863
|
var createCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("create_creative_variant");
|
|
29830
|
-
var LandingSectionKeyEnum =
|
|
29864
|
+
var LandingSectionKeyEnum = z126.enum([
|
|
29831
29865
|
"problem_hook",
|
|
29832
29866
|
"problem_cost",
|
|
29833
29867
|
"solution_mechanism",
|
|
@@ -29867,24 +29901,24 @@ var LandingSectionKeyEnum = z125.enum([
|
|
|
29867
29901
|
"lifestyle_fit",
|
|
29868
29902
|
"join_lifestyle"
|
|
29869
29903
|
]);
|
|
29870
|
-
var CreativeVariantKindEnum =
|
|
29904
|
+
var CreativeVariantKindEnum = z126.enum([
|
|
29871
29905
|
"video",
|
|
29872
29906
|
"image",
|
|
29873
29907
|
"landing"
|
|
29874
29908
|
]);
|
|
29875
|
-
var EstimateCreativeVariantInputBase =
|
|
29876
|
-
sourceTaskId:
|
|
29909
|
+
var EstimateCreativeVariantInputBase = z126.object({
|
|
29910
|
+
sourceTaskId: z126.string().uuid().describe("ID de la tarea original completada."),
|
|
29877
29911
|
kind: CreativeVariantKindEnum,
|
|
29878
29912
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
29879
29913
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
29880
|
-
nVariants:
|
|
29914
|
+
nVariants: z126.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
29881
29915
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
29882
|
-
instructions:
|
|
29916
|
+
instructions: z126.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.")
|
|
29883
29917
|
}).strict();
|
|
29884
29918
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
29885
29919
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
29886
29920
|
ctx.addIssue({
|
|
29887
|
-
code:
|
|
29921
|
+
code: z126.ZodIssueCode.custom,
|
|
29888
29922
|
path: [
|
|
29889
29923
|
"nVariants"
|
|
29890
29924
|
],
|
|
@@ -29894,7 +29928,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29894
29928
|
if (input.kind !== "video") {
|
|
29895
29929
|
if (input.videoPreset !== void 0) {
|
|
29896
29930
|
ctx.addIssue({
|
|
29897
|
-
code:
|
|
29931
|
+
code: z126.ZodIssueCode.custom,
|
|
29898
29932
|
path: [
|
|
29899
29933
|
"videoPreset"
|
|
29900
29934
|
],
|
|
@@ -29903,7 +29937,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29903
29937
|
}
|
|
29904
29938
|
if (input.videoResolution !== void 0) {
|
|
29905
29939
|
ctx.addIssue({
|
|
29906
|
-
code:
|
|
29940
|
+
code: z126.ZodIssueCode.custom,
|
|
29907
29941
|
path: [
|
|
29908
29942
|
"videoResolution"
|
|
29909
29943
|
],
|
|
@@ -29913,7 +29947,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29913
29947
|
}
|
|
29914
29948
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
29915
29949
|
ctx.addIssue({
|
|
29916
|
-
code:
|
|
29950
|
+
code: z126.ZodIssueCode.custom,
|
|
29917
29951
|
path: [
|
|
29918
29952
|
"nVariants"
|
|
29919
29953
|
],
|
|
@@ -29923,7 +29957,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29923
29957
|
if (input.kind !== "landing") {
|
|
29924
29958
|
if (input.sectionKey !== void 0) {
|
|
29925
29959
|
ctx.addIssue({
|
|
29926
|
-
code:
|
|
29960
|
+
code: z126.ZodIssueCode.custom,
|
|
29927
29961
|
path: [
|
|
29928
29962
|
"sectionKey"
|
|
29929
29963
|
],
|
|
@@ -29932,7 +29966,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29932
29966
|
}
|
|
29933
29967
|
if (input.instructions !== void 0) {
|
|
29934
29968
|
ctx.addIssue({
|
|
29935
|
-
code:
|
|
29969
|
+
code: z126.ZodIssueCode.custom,
|
|
29936
29970
|
path: [
|
|
29937
29971
|
"instructions"
|
|
29938
29972
|
],
|
|
@@ -29941,31 +29975,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29941
29975
|
}
|
|
29942
29976
|
}
|
|
29943
29977
|
});
|
|
29944
|
-
var CreateCreativeVariantInputBase =
|
|
29945
|
-
sourceTaskId:
|
|
29978
|
+
var CreateCreativeVariantInputBase = z126.object({
|
|
29979
|
+
sourceTaskId: z126.string().uuid().describe("ID de la tarea original completada."),
|
|
29946
29980
|
kind: CreativeVariantKindEnum,
|
|
29947
|
-
confirm:
|
|
29948
|
-
clientRequestId:
|
|
29949
|
-
angle:
|
|
29950
|
-
buyerPersona:
|
|
29951
|
-
scriptNotes:
|
|
29952
|
-
creativeDirection:
|
|
29981
|
+
confirm: z126.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
29982
|
+
clientRequestId: z126.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."),
|
|
29983
|
+
angle: z126.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
29984
|
+
buyerPersona: z126.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
29985
|
+
scriptNotes: z126.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
29986
|
+
creativeDirection: z126.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
29953
29987
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
29954
29988
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
29955
|
-
generateAiMusic:
|
|
29956
|
-
nVariants:
|
|
29989
|
+
generateAiMusic: z126.boolean().optional().describe("Opcional para video."),
|
|
29990
|
+
nVariants: z126.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
29957
29991
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
29958
29992
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
29959
29993
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
29960
29994
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
29961
|
-
styleMix:
|
|
29995
|
+
styleMix: z126.boolean().optional().describe("Opcional para imagen."),
|
|
29962
29996
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
29963
|
-
instructions:
|
|
29997
|
+
instructions: z126.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.")
|
|
29964
29998
|
}).strict();
|
|
29965
29999
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
29966
30000
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
29967
30001
|
ctx.addIssue({
|
|
29968
|
-
code:
|
|
30002
|
+
code: z126.ZodIssueCode.custom,
|
|
29969
30003
|
path: [
|
|
29970
30004
|
"angle"
|
|
29971
30005
|
],
|
|
@@ -29974,7 +30008,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
29974
30008
|
}
|
|
29975
30009
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
29976
30010
|
ctx.addIssue({
|
|
29977
|
-
code:
|
|
30011
|
+
code: z126.ZodIssueCode.custom,
|
|
29978
30012
|
path: [
|
|
29979
30013
|
"instructions"
|
|
29980
30014
|
],
|
|
@@ -30020,7 +30054,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
30020
30054
|
for (const field of fieldsByKind[input.kind]) {
|
|
30021
30055
|
if (input[field] !== void 0) {
|
|
30022
30056
|
ctx.addIssue({
|
|
30023
|
-
code:
|
|
30057
|
+
code: z126.ZodIssueCode.custom,
|
|
30024
30058
|
path: [
|
|
30025
30059
|
field
|
|
30026
30060
|
],
|
|
@@ -30076,8 +30110,10 @@ function registerCreativeVariantTools(server, api) {
|
|
|
30076
30110
|
inputSchema: CreateCreativeVariantInputBase.shape,
|
|
30077
30111
|
annotations: CREATES_PAID
|
|
30078
30112
|
}, async (input) => {
|
|
30113
|
+
let sourceTaskId;
|
|
30079
30114
|
try {
|
|
30080
30115
|
const validatedInput = parseVariantInput(CreateCreativeVariantInput, input);
|
|
30116
|
+
sourceTaskId = validatedInput.sourceTaskId;
|
|
30081
30117
|
const estimateInput = EstimateCreativeVariantInput.parse({
|
|
30082
30118
|
sourceTaskId: validatedInput.sourceTaskId,
|
|
30083
30119
|
kind: validatedInput.kind,
|
|
@@ -30088,7 +30124,7 @@ function registerCreativeVariantTools(server, api) {
|
|
|
30088
30124
|
instructions: validatedInput.instructions
|
|
30089
30125
|
});
|
|
30090
30126
|
const estimate = await api.postJson(`/v1/tasks/${encodeURIComponent(validatedInput.sourceTaskId)}/creative-variants/estimate`, estimatePayload(estimateInput));
|
|
30091
|
-
if (!estimate.hasSufficientBalance) {
|
|
30127
|
+
if (validatedInput.kind === "landing" && !validatedInput.sectionKey && !estimate.hasSufficientBalance) {
|
|
30092
30128
|
throw mcp(McpErrorCode.INSUFFICIENT_CREDITS, `Saldo insuficiente. La variante requiere hasta ${estimate.estimatedMaxCostCredits} cr\xE9ditos y tu saldo es ${estimate.userBalanceCredits}.`);
|
|
30093
30129
|
}
|
|
30094
30130
|
let sectionKey = validatedInput.sectionKey;
|
|
@@ -30146,20 +30182,43 @@ function registerCreativeVariantTools(server, api) {
|
|
|
30146
30182
|
estimate
|
|
30147
30183
|
});
|
|
30148
30184
|
} catch (error) {
|
|
30149
|
-
|
|
30185
|
+
const translated = translateError(error);
|
|
30186
|
+
if (!sourceTaskId) throw translated;
|
|
30187
|
+
const details = isRecord3(translated.data) ? translated.data : {};
|
|
30188
|
+
return {
|
|
30189
|
+
...asText({
|
|
30190
|
+
error: {
|
|
30191
|
+
code: typeof details.code === "string" ? details.code : McpErrorCode.INTERNAL,
|
|
30192
|
+
message: translated.message,
|
|
30193
|
+
...typeof details.variantTaskId === "string" ? {
|
|
30194
|
+
variantTaskId: details.variantTaskId
|
|
30195
|
+
} : {}
|
|
30196
|
+
},
|
|
30197
|
+
// A classification or transport failure does not prove that no
|
|
30198
|
+
// child was admitted. Inspect owned children before another buy.
|
|
30199
|
+
nextAction: {
|
|
30200
|
+
tool: "get_task_status",
|
|
30201
|
+
arguments: {
|
|
30202
|
+
taskId: sourceTaskId
|
|
30203
|
+
},
|
|
30204
|
+
instruction: "Revisa las variantes de la tarea original antes de iniciar otro intento pagado. No crees otra variante autom\xE1ticamente."
|
|
30205
|
+
}
|
|
30206
|
+
}),
|
|
30207
|
+
isError: true
|
|
30208
|
+
};
|
|
30150
30209
|
}
|
|
30151
30210
|
});
|
|
30152
30211
|
}
|
|
30153
30212
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
30154
30213
|
|
|
30155
30214
|
// src/tools/script-review.ts
|
|
30156
|
-
import { z as
|
|
30215
|
+
import { z as z127 } from "zod";
|
|
30157
30216
|
var getVideoScriptGuidance = getProductMakerCapabilityModelGuidance("get_video_script");
|
|
30158
30217
|
var regenerateVideoScriptGuidance = getProductMakerCapabilityModelGuidance("regenerate_video_script");
|
|
30159
30218
|
var approveVideoScriptGuidance = getProductMakerCapabilityModelGuidance("approve_video_script");
|
|
30160
|
-
var GetVideoScriptInput =
|
|
30161
|
-
taskId:
|
|
30162
|
-
angleKey:
|
|
30219
|
+
var GetVideoScriptInput = z127.object({
|
|
30220
|
+
taskId: z127.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
30221
|
+
angleKey: z127.string().min(1).optional().describe('Clave persistida del \xE1ngulo cuyo guion se quiere leer (formato "angle_0", "angle_1", ...). Usa exactamente el angleKey recibido en reviewNextAction.calls al aprobar \xE1ngulos, incluso si s\xF3lo elegiste uno. Si se omite, el backend resuelve a "angle_0" literal; elegir el segundo candidato no crea angle_0. No derives esta clave del n\xFAmero visible del candidato.')
|
|
30163
30222
|
}).strict();
|
|
30164
30223
|
var ApproveVideoScriptInput = ApproveVideoScriptCapabilityRequest;
|
|
30165
30224
|
var RegenerateVideoScriptInput = RegenerateVideoScriptCapabilityRequest.extend({
|
|
@@ -30167,7 +30226,7 @@ var RegenerateVideoScriptInput = RegenerateVideoScriptCapabilityRequest.extend({
|
|
|
30167
30226
|
});
|
|
30168
30227
|
function registerScriptReviewTools(server, api) {
|
|
30169
30228
|
server.registerTool("get_video_script", {
|
|
30170
|
-
description: getVideoScriptGuidance.summary + ' S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId?, persuasiveFunction?, winnerVisual?, expectedVisual? }], error? }. En escenas copiadas de un winner, `persuasiveFunction` explica qu\xE9 logra ese beat, `winnerVisual` conserva la observaci\xF3n fuente y `expectedVisual` describe nuestra adaptaci\xF3n localizada; muestra los tres junto al di\xE1logo para que una edici\xF3n no desaline\xE9 guion y visual. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado; mu\xE9straselo al usuario.\n\nREGLA DURA: status="ready" es un estado terminal de interacci\xF3n. Presenta el guion una sola vez y NO vuelvas a llamar get_video_script ni get_task_status hasta que el usuario responda aprobar, editar o regenerar. La respuesta a\xF1ade interactionState="awaiting_user_review" y nextAction="present_script_and_wait".\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y llama este tool; su campo `status` es la se\xF1al real:\n- "ready" \u2192 presenta todas las escenas y DETENTE hasta la respuesta del usuario.\n- "not_ready" \u2192 espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle consulta otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar.\n- "error" \u2192
|
|
30229
|
+
description: getVideoScriptGuidance.summary + ' S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId?, persuasiveFunction?, winnerVisual?, expectedVisual? }], error? }. En escenas copiadas de un winner, `persuasiveFunction` explica qu\xE9 logra ese beat, `winnerVisual` conserva la observaci\xF3n fuente y `expectedVisual` describe nuestra adaptaci\xF3n localizada; muestra los tres junto al di\xE1logo para que una edici\xF3n no desaline\xE9 guion y visual. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado; mu\xE9straselo al usuario.\n\nREGLA DURA: status="ready" es un estado terminal de interacci\xF3n. Presenta el guion una sola vez y NO vuelvas a llamar get_video_script ni get_task_status hasta que el usuario responda aprobar, editar o regenerar. La respuesta a\xF1ade interactionState="awaiting_user_review" y nextAction="present_script_and_wait".\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y llama este tool; su campo `status` es la se\xF1al real:\n- "ready" \u2192 presenta todas las escenas y DETENTE hasta la respuesta del usuario.\n- "not_ready" \u2192 espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle consulta otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar.\n- "error" \u2192 consulta `get_task_status` antes de ofrecer recuperaci\xF3n. No regeneres autom\xE1ticamente una tarea terminal.\n\nANGLE KEY: usa las claves expl\xEDcitas de reviewNextAction.calls, incluso si s\xF3lo seleccionaste un candidato. Cuando uno est\xE9 ready, detente en ese \xE1ngulo y espera la decisi\xF3n antes de consultar otro.',
|
|
30171
30230
|
inputSchema: GetVideoScriptInput.shape,
|
|
30172
30231
|
annotations: READS_INTERNAL
|
|
30173
30232
|
}, async (input) => {
|
|
@@ -30175,6 +30234,28 @@ function registerScriptReviewTools(server, api) {
|
|
|
30175
30234
|
const qs = input.angleKey ? `?angleKey=${encodeURIComponent(input.angleKey)}` : "";
|
|
30176
30235
|
const r = await api.getJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/video-script${qs}`);
|
|
30177
30236
|
const review = renderVideoScriptMarkdown(r);
|
|
30237
|
+
if (review && typeof r === "object" && r !== null && "status" in r && r.status === "error") {
|
|
30238
|
+
const error = "error" in r && typeof r.error === "string" ? escapeMarkdownText(r.error) : "No se pudo preparar este guion.";
|
|
30239
|
+
return {
|
|
30240
|
+
content: [
|
|
30241
|
+
{
|
|
30242
|
+
type: "text",
|
|
30243
|
+
text: `## No se pudo preparar el guion
|
|
30244
|
+
|
|
30245
|
+
${error}
|
|
30246
|
+
|
|
30247
|
+
Consulta get_task_status con esta tarea antes de reintentar. Si la tarea termin\xF3 en error, sigue su recuperaci\xF3n; si sigue activa y s\xF3lo fall\xF3 el guion, ofrece regenerarlo con confirmaci\xF3n del usuario. No inicies una regeneraci\xF3n pagada autom\xE1ticamente.`
|
|
30248
|
+
}
|
|
30249
|
+
],
|
|
30250
|
+
structuredContent: {
|
|
30251
|
+
...r,
|
|
30252
|
+
nextAction: "get_task_status",
|
|
30253
|
+
nextActionArguments: {
|
|
30254
|
+
taskId: input.taskId
|
|
30255
|
+
}
|
|
30256
|
+
}
|
|
30257
|
+
};
|
|
30258
|
+
}
|
|
30178
30259
|
return review ? {
|
|
30179
30260
|
content: [
|
|
30180
30261
|
{
|
|
@@ -30203,7 +30284,7 @@ function registerScriptReviewTools(server, api) {
|
|
|
30203
30284
|
}
|
|
30204
30285
|
});
|
|
30205
30286
|
server.registerTool("regenerate_video_script", {
|
|
30206
|
-
description: regenerateVideoScriptGuidance.summary + ' \xDAsalo cuando:\n- get_video_script devuelve status="error"
|
|
30287
|
+
description: regenerateVideoScriptGuidance.summary + ' \xDAsalo cuando:\n- get_video_script devuelve status="error", get_task_status confirma que la tarea sigue activa y el usuario confirma reintentar el guion.\n- El usuario rechaza el guion entero y prefiere reintentar (no editar l\xEDnea por l\xEDnea).\n- Quieres probar otra narrativa (pasa `narrativeStyle`).\n\nFlujo: llama este tool \u2192 polea get_video_script hasta que vuelva a status="ready" \u2192 mu\xE9strale el nuevo guion al usuario \u2192 approve_video_script.\n\nIMPORTANTE: regenerar CUESTA cr\xE9ditos de LLM. No lo invoques sin confirmaci\xF3n del usuario, incluso si el guion est\xE1 en error. Para una tarea terminal, sigue primero la recuperaci\xF3n de get_task_status.\n\nMULTI-ANGLE: pasa el mismo `angleKey` que usaste con get_video_script.',
|
|
30207
30288
|
inputSchema: RegenerateVideoScriptInput.shape,
|
|
30208
30289
|
annotations: OVERWRITES_EXTERNAL
|
|
30209
30290
|
}, async (input) => {
|
|
@@ -30242,7 +30323,7 @@ function registerScriptReviewTools(server, api) {
|
|
|
30242
30323
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
30243
30324
|
|
|
30244
30325
|
// src/tools/connections.ts
|
|
30245
|
-
import { z as
|
|
30326
|
+
import { z as z128 } from "zod";
|
|
30246
30327
|
var listShopifyShopsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_shops");
|
|
30247
30328
|
var listShopifyProductsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_products");
|
|
30248
30329
|
var listMetaAdAccountsGuidance = getProductMakerCapabilityModelGuidance("list_meta_ad_accounts");
|
|
@@ -30251,7 +30332,7 @@ var listTikTokAdvertisersGuidance = getProductMakerCapabilityModelGuidance("list
|
|
|
30251
30332
|
function registerConnectionTools(server, api) {
|
|
30252
30333
|
server.registerTool("list_shopify_shops", {
|
|
30253
30334
|
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.",
|
|
30254
|
-
inputSchema:
|
|
30335
|
+
inputSchema: z128.object({}).shape,
|
|
30255
30336
|
annotations: READS_INTERNAL
|
|
30256
30337
|
}, async () => {
|
|
30257
30338
|
try {
|
|
@@ -30263,10 +30344,10 @@ function registerConnectionTools(server, api) {
|
|
|
30263
30344
|
});
|
|
30264
30345
|
server.registerTool("list_shopify_products", {
|
|
30265
30346
|
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.',
|
|
30266
|
-
inputSchema:
|
|
30267
|
-
shopDomain:
|
|
30268
|
-
query:
|
|
30269
|
-
taskId:
|
|
30347
|
+
inputSchema: z128.object({
|
|
30348
|
+
shopDomain: z128.string().min(3).max(120),
|
|
30349
|
+
query: z128.string().trim().max(120).optional(),
|
|
30350
|
+
taskId: z128.string().uuid().optional()
|
|
30270
30351
|
}).shape,
|
|
30271
30352
|
annotations: READS_EXTERNAL
|
|
30272
30353
|
}, async ({ shopDomain, query, taskId }) => {
|
|
@@ -30284,7 +30365,7 @@ function registerConnectionTools(server, api) {
|
|
|
30284
30365
|
});
|
|
30285
30366
|
server.registerTool("list_meta_ad_accounts", {
|
|
30286
30367
|
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.",
|
|
30287
|
-
inputSchema:
|
|
30368
|
+
inputSchema: z128.object({}).shape,
|
|
30288
30369
|
annotations: READS_EXTERNAL
|
|
30289
30370
|
}, async () => {
|
|
30290
30371
|
try {
|
|
@@ -30296,7 +30377,7 @@ function registerConnectionTools(server, api) {
|
|
|
30296
30377
|
});
|
|
30297
30378
|
server.registerTool("list_meta_destinations", {
|
|
30298
30379
|
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.",
|
|
30299
|
-
inputSchema:
|
|
30380
|
+
inputSchema: z128.object({}).shape,
|
|
30300
30381
|
annotations: READS_EXTERNAL,
|
|
30301
30382
|
_meta: {
|
|
30302
30383
|
"openai/widgetAccessible": true
|
|
@@ -30311,7 +30392,7 @@ function registerConnectionTools(server, api) {
|
|
|
30311
30392
|
});
|
|
30312
30393
|
server.registerTool("list_tiktok_advertisers", {
|
|
30313
30394
|
description: listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.",
|
|
30314
|
-
inputSchema:
|
|
30395
|
+
inputSchema: z128.object({}).shape,
|
|
30315
30396
|
annotations: READS_EXTERNAL
|
|
30316
30397
|
}, async () => {
|
|
30317
30398
|
try {
|
|
@@ -30325,7 +30406,7 @@ function registerConnectionTools(server, api) {
|
|
|
30325
30406
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
30326
30407
|
|
|
30327
30408
|
// src/tools/actors.ts
|
|
30328
|
-
import { z as
|
|
30409
|
+
import { z as z129 } from "zod";
|
|
30329
30410
|
var listActorsGuidance = getProductMakerCapabilityModelGuidance("list_actors");
|
|
30330
30411
|
var actorVoiceGuidance = {
|
|
30331
30412
|
capabilities: getProductMakerCapabilityModelGuidance("get_actor_voice_capabilities"),
|
|
@@ -30365,7 +30446,7 @@ function sanitizeVoiceResult(value) {
|
|
|
30365
30446
|
__name(sanitizeVoiceResult, "sanitizeVoiceResult");
|
|
30366
30447
|
function registerActorTools(server, api) {
|
|
30367
30448
|
const actor = {
|
|
30368
|
-
actorId:
|
|
30449
|
+
actorId: z129.string().min(1).max(64)
|
|
30369
30450
|
};
|
|
30370
30451
|
const safe = /* @__PURE__ */ __name(async (request) => {
|
|
30371
30452
|
try {
|
|
@@ -30394,8 +30475,8 @@ function registerActorTools(server, api) {
|
|
|
30394
30475
|
inputSchema: {
|
|
30395
30476
|
...actor,
|
|
30396
30477
|
draft: ActorVoiceDraftSchema,
|
|
30397
|
-
requestKey:
|
|
30398
|
-
acceptedQuoteId:
|
|
30478
|
+
requestKey: z129.string().trim().min(1).max(128),
|
|
30479
|
+
acceptedQuoteId: z129.string().trim().min(1).max(128)
|
|
30399
30480
|
},
|
|
30400
30481
|
annotations: CREATES_PAID
|
|
30401
30482
|
}, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/previews`, body)));
|
|
@@ -30403,7 +30484,7 @@ function registerActorTools(server, api) {
|
|
|
30403
30484
|
description: actorVoiceGuidance.get.summary,
|
|
30404
30485
|
inputSchema: {
|
|
30405
30486
|
...actor,
|
|
30406
|
-
candidateId:
|
|
30487
|
+
candidateId: z129.string().min(1).max(64)
|
|
30407
30488
|
},
|
|
30408
30489
|
// Polling can resume the first paid submission of an accepted quote.
|
|
30409
30490
|
// The immutable candidate/operation key keeps recovery idempotent.
|
|
@@ -30416,16 +30497,16 @@ function registerActorTools(server, api) {
|
|
|
30416
30497
|
description: actorVoiceGuidance.approve.summary,
|
|
30417
30498
|
inputSchema: {
|
|
30418
30499
|
...actor,
|
|
30419
|
-
candidateId:
|
|
30420
|
-
expectedRevision:
|
|
30421
|
-
expectedApprovedRevision:
|
|
30500
|
+
candidateId: z129.string().min(1).max(64),
|
|
30501
|
+
expectedRevision: z129.number().int().positive(),
|
|
30502
|
+
expectedApprovedRevision: z129.number().int().positive().nullable().optional()
|
|
30422
30503
|
},
|
|
30423
30504
|
// Replaces the selected voice and withdraws the old catalog grants.
|
|
30424
30505
|
annotations: OVERWRITES_INTERNAL
|
|
30425
30506
|
}, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/approve`, body)));
|
|
30426
30507
|
server.registerTool("list_actors", {
|
|
30427
30508
|
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.",
|
|
30428
|
-
inputSchema:
|
|
30509
|
+
inputSchema: z129.object({}).shape,
|
|
30429
30510
|
annotations: READS_INTERNAL
|
|
30430
30511
|
}, async () => {
|
|
30431
30512
|
try {
|
|
@@ -30458,7 +30539,7 @@ function registerActorTools(server, api) {
|
|
|
30458
30539
|
__name(registerActorTools, "registerActorTools");
|
|
30459
30540
|
|
|
30460
30541
|
// src/tools/publish.ts
|
|
30461
|
-
import { z as
|
|
30542
|
+
import { z as z130 } from "zod";
|
|
30462
30543
|
|
|
30463
30544
|
// src/tools/meta-publish-attempt-id.ts
|
|
30464
30545
|
var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
|
|
@@ -30497,40 +30578,40 @@ var publishToShopifyGuidance = getProductMakerCapabilityModelGuidance("publish_t
|
|
|
30497
30578
|
var publishToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_to_meta");
|
|
30498
30579
|
var publishCreativesToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_creatives_to_meta");
|
|
30499
30580
|
var publishToTikTokGuidance = getProductMakerCapabilityModelGuidance("publish_to_tiktok");
|
|
30500
|
-
var ShopifyInput =
|
|
30501
|
-
taskId:
|
|
30502
|
-
shopDomain:
|
|
30581
|
+
var ShopifyInput = z130.object({
|
|
30582
|
+
taskId: z130.string().min(1),
|
|
30583
|
+
shopDomain: z130.string().min(1),
|
|
30503
30584
|
/** Override: number of landing images to include. Default = ALL images the
|
|
30504
30585
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
30505
30586
|
* generously here; the backend validates each index against
|
|
30506
30587
|
* landing.images.length and rejects out-of-range. */
|
|
30507
|
-
numLandingImages:
|
|
30508
|
-
variantIndex:
|
|
30588
|
+
numLandingImages: z130.number().int().min(1).max(20).optional(),
|
|
30589
|
+
variantIndex: z130.number().int().min(0).optional(),
|
|
30509
30590
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
30510
30591
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
30511
30592
|
* a single-option product page. Defaults to false. */
|
|
30512
|
-
showVariantPicker:
|
|
30593
|
+
showVariantPicker: z130.boolean().optional(),
|
|
30513
30594
|
/** Default `auto` updates the product previously published for this task and
|
|
30514
30595
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
30515
30596
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
30516
|
-
publicationMode:
|
|
30597
|
+
publicationMode: z130.enum([
|
|
30517
30598
|
"auto",
|
|
30518
30599
|
"existing",
|
|
30519
30600
|
"new"
|
|
30520
30601
|
]).optional(),
|
|
30521
30602
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
30522
30603
|
* publicationMode is `existing`. */
|
|
30523
|
-
targetProductId:
|
|
30604
|
+
targetProductId: z130.string().trim().min(1).max(300).optional(),
|
|
30524
30605
|
/** Add an announcement bar text above add-to-cart (optional). */
|
|
30525
|
-
announcementBarEnabled:
|
|
30526
|
-
announcementBarText:
|
|
30606
|
+
announcementBarEnabled: z130.boolean().optional(),
|
|
30607
|
+
announcementBarText: z130.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
|
|
30527
30608
|
commerceCorrection: PublishCommerceCorrectionSchema.optional().describe("Complete seller-confirmed commerce values, with the revision and fingerprint returned in recovery. Never invent missing prices or convert currency."),
|
|
30528
|
-
confirmed:
|
|
30609
|
+
confirmed: z130.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
|
|
30529
30610
|
});
|
|
30530
30611
|
var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value, ctx) => {
|
|
30531
30612
|
if (value.commerceCorrection && value.confirmed !== true) {
|
|
30532
30613
|
ctx.addIssue({
|
|
30533
|
-
code:
|
|
30614
|
+
code: z130.ZodIssueCode.custom,
|
|
30534
30615
|
path: [
|
|
30535
30616
|
"confirmed"
|
|
30536
30617
|
],
|
|
@@ -30539,7 +30620,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
30539
30620
|
}
|
|
30540
30621
|
if (value.announcementBarEnabled && !value.announcementBarText) {
|
|
30541
30622
|
ctx.addIssue({
|
|
30542
|
-
code:
|
|
30623
|
+
code: z130.ZodIssueCode.custom,
|
|
30543
30624
|
path: [
|
|
30544
30625
|
"announcementBarText"
|
|
30545
30626
|
],
|
|
@@ -30548,7 +30629,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
30548
30629
|
}
|
|
30549
30630
|
if (!value.announcementBarEnabled && value.announcementBarText) {
|
|
30550
30631
|
ctx.addIssue({
|
|
30551
|
-
code:
|
|
30632
|
+
code: z130.ZodIssueCode.custom,
|
|
30552
30633
|
path: [
|
|
30553
30634
|
"announcementBarText"
|
|
30554
30635
|
],
|
|
@@ -30562,90 +30643,90 @@ function parseShopifyInput(rawInput) {
|
|
|
30562
30643
|
return parsed.data;
|
|
30563
30644
|
}
|
|
30564
30645
|
__name(parseShopifyInput, "parseShopifyInput");
|
|
30565
|
-
var McpCampaignConfig =
|
|
30646
|
+
var McpCampaignConfig = z130.object({
|
|
30566
30647
|
/** Legacy USD amount. Meta retains its existing conversion; the TikTok
|
|
30567
30648
|
* schema below restricts this input to USD advertiser accounts. */
|
|
30568
|
-
dailyBudgetUsd:
|
|
30649
|
+
dailyBudgetUsd: z130.number().positive().optional(),
|
|
30569
30650
|
/** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
|
|
30570
30651
|
* normalizes at validation). Meta accepts max 1 (single-country ad set).
|
|
30571
30652
|
* TikTok takes only the first entry. */
|
|
30572
|
-
targetCountries:
|
|
30573
|
-
campaignName:
|
|
30653
|
+
targetCountries: z130.array(CountryIso2Schema).max(1).optional(),
|
|
30654
|
+
campaignName: z130.string().trim().min(1).max(100).optional()
|
|
30574
30655
|
});
|
|
30575
30656
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
30576
|
-
var MetaInput =
|
|
30577
|
-
taskId:
|
|
30657
|
+
var MetaInput = z130.object({
|
|
30658
|
+
taskId: z130.string().min(1),
|
|
30578
30659
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
30579
|
-
portfolioId:
|
|
30660
|
+
portfolioId: z130.string().trim().min(1),
|
|
30580
30661
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
30581
|
-
pageId:
|
|
30662
|
+
pageId: z130.string().trim().min(1),
|
|
30582
30663
|
adAccountId: McpAdAccountId,
|
|
30583
|
-
pixelId:
|
|
30584
|
-
landingUrl:
|
|
30664
|
+
pixelId: z130.string().min(1).optional(),
|
|
30665
|
+
landingUrl: z130.string().url().optional(),
|
|
30585
30666
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
30586
30667
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
30587
|
-
angleIndexes:
|
|
30668
|
+
angleIndexes: z130.array(z130.number().int().nonnegative()).optional(),
|
|
30588
30669
|
campaignConfig: McpCampaignConfig.optional(),
|
|
30589
30670
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
30590
30671
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
30591
30672
|
* (Meta's primary-text limit). */
|
|
30592
|
-
primaryText:
|
|
30673
|
+
primaryText: z130.string().trim().min(1).max(500).optional(),
|
|
30593
30674
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
30594
30675
|
* (wa.me) instead of a landing page. */
|
|
30595
|
-
phoneNumber:
|
|
30676
|
+
phoneNumber: z130.string().min(8).max(32).optional()
|
|
30596
30677
|
});
|
|
30597
|
-
var GroupedTaskSource =
|
|
30598
|
-
type:
|
|
30599
|
-
taskId:
|
|
30600
|
-
angleIndex:
|
|
30601
|
-
videoVariant:
|
|
30678
|
+
var GroupedTaskSource = z130.object({
|
|
30679
|
+
type: z130.literal("task"),
|
|
30680
|
+
taskId: z130.string().min(1),
|
|
30681
|
+
angleIndex: z130.number().int().min(0).optional(),
|
|
30682
|
+
videoVariant: z130.enum([
|
|
30602
30683
|
"with_subtitles",
|
|
30603
30684
|
"without_subtitles"
|
|
30604
30685
|
]).optional(),
|
|
30605
|
-
imageVariantIndex:
|
|
30606
|
-
});
|
|
30607
|
-
var GroupedMediaSource =
|
|
30608
|
-
type:
|
|
30609
|
-
url:
|
|
30610
|
-
path:
|
|
30611
|
-
thumbnailUrl:
|
|
30612
|
-
thumbnailPath:
|
|
30686
|
+
imageVariantIndex: z130.number().int().min(0).optional()
|
|
30687
|
+
});
|
|
30688
|
+
var GroupedMediaSource = z130.object({
|
|
30689
|
+
type: z130.literal("media"),
|
|
30690
|
+
url: z130.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
|
|
30691
|
+
path: z130.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".'),
|
|
30692
|
+
thumbnailUrl: z130.string().url().optional(),
|
|
30693
|
+
thumbnailPath: z130.string().min(1).optional()
|
|
30613
30694
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
30614
30695
|
message: "media source needs exactly one of `url` or `path`"
|
|
30615
30696
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
30616
30697
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
30617
30698
|
});
|
|
30618
|
-
var GroupedUploadedAssetSource =
|
|
30619
|
-
type:
|
|
30699
|
+
var GroupedUploadedAssetSource = z130.object({
|
|
30700
|
+
type: z130.literal("asset"),
|
|
30620
30701
|
/** Returned by check_media_upload. ProductMaker resolves ownership server-side. */
|
|
30621
|
-
assetId:
|
|
30622
|
-
thumbnailAssetId:
|
|
30702
|
+
assetId: z130.string().uuid(),
|
|
30703
|
+
thumbnailAssetId: z130.string().uuid().optional()
|
|
30623
30704
|
});
|
|
30624
|
-
var GroupedCreative =
|
|
30625
|
-
type:
|
|
30705
|
+
var GroupedCreative = z130.object({
|
|
30706
|
+
type: z130.enum([
|
|
30626
30707
|
"image",
|
|
30627
30708
|
"video"
|
|
30628
30709
|
]),
|
|
30629
|
-
source:
|
|
30710
|
+
source: z130.union([
|
|
30630
30711
|
GroupedTaskSource,
|
|
30631
30712
|
GroupedMediaSource,
|
|
30632
30713
|
GroupedUploadedAssetSource
|
|
30633
30714
|
]),
|
|
30634
|
-
title:
|
|
30635
|
-
description:
|
|
30715
|
+
title: z130.string().max(120).optional(),
|
|
30716
|
+
description: z130.string().max(500).optional(),
|
|
30636
30717
|
cta: MetaCtaType.optional(),
|
|
30637
|
-
creativeId:
|
|
30718
|
+
creativeId: z130.string().min(1).optional()
|
|
30638
30719
|
});
|
|
30639
|
-
var GroupedCampaignConfigBase =
|
|
30720
|
+
var GroupedCampaignConfigBase = z130.object({
|
|
30640
30721
|
campaignType: MetaCampaignType.default("sales"),
|
|
30641
30722
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
30642
30723
|
budgetMode: MetaBudgetMode.optional(),
|
|
30643
|
-
campaignDailyBudget:
|
|
30644
|
-
adSets:
|
|
30645
|
-
tempId:
|
|
30646
|
-
name:
|
|
30647
|
-
dailyBudget:
|
|
30648
|
-
creativeIds:
|
|
30724
|
+
campaignDailyBudget: z130.number().int().positive().optional(),
|
|
30725
|
+
adSets: z130.array(z130.object({
|
|
30726
|
+
tempId: z130.string().min(1).max(80),
|
|
30727
|
+
name: z130.string().min(1).max(100),
|
|
30728
|
+
dailyBudget: z130.number().int().positive().optional(),
|
|
30729
|
+
creativeIds: z130.array(z130.string().min(1)).min(1)
|
|
30649
30730
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
30650
30731
|
/**
|
|
30651
30732
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -30653,17 +30734,17 @@ var GroupedCampaignConfigBase = z129.object({
|
|
|
30653
30734
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
30654
30735
|
* the API since #849 — unreachable from this tool.
|
|
30655
30736
|
*/
|
|
30656
|
-
destination:
|
|
30657
|
-
|
|
30658
|
-
type:
|
|
30659
|
-
landingUrl:
|
|
30737
|
+
destination: z130.discriminatedUnion("type", [
|
|
30738
|
+
z130.object({
|
|
30739
|
+
type: z130.literal("landing"),
|
|
30740
|
+
landingUrl: z130.string().url()
|
|
30660
30741
|
}),
|
|
30661
|
-
|
|
30662
|
-
type:
|
|
30663
|
-
phoneNumber:
|
|
30742
|
+
z130.object({
|
|
30743
|
+
type: z130.literal("whatsapp"),
|
|
30744
|
+
phoneNumber: z130.string().min(8).max(32)
|
|
30664
30745
|
})
|
|
30665
30746
|
]),
|
|
30666
|
-
saveAsDefault:
|
|
30747
|
+
saveAsDefault: z130.boolean().default(false),
|
|
30667
30748
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
30668
30749
|
});
|
|
30669
30750
|
function resolveBudgetMode(value) {
|
|
@@ -30677,7 +30758,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
30677
30758
|
const budgetMode = resolveBudgetMode(value);
|
|
30678
30759
|
if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
30679
30760
|
ctx.addIssue({
|
|
30680
|
-
code:
|
|
30761
|
+
code: z130.ZodIssueCode.custom,
|
|
30681
30762
|
path: [
|
|
30682
30763
|
"campaignDailyBudget"
|
|
30683
30764
|
],
|
|
@@ -30688,7 +30769,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
30688
30769
|
value.adSets.forEach((adSet, index) => {
|
|
30689
30770
|
if (adSet.dailyBudget === void 0) {
|
|
30690
30771
|
ctx.addIssue({
|
|
30691
|
-
code:
|
|
30772
|
+
code: z130.ZodIssueCode.custom,
|
|
30692
30773
|
path: [
|
|
30693
30774
|
"adSets",
|
|
30694
30775
|
index,
|
|
@@ -30703,25 +30784,25 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
30703
30784
|
...value,
|
|
30704
30785
|
budgetMode: resolveBudgetMode(value)
|
|
30705
30786
|
}));
|
|
30706
|
-
var PublishCreativesToMetaInputBase =
|
|
30707
|
-
creatives:
|
|
30787
|
+
var PublishCreativesToMetaInputBase = z130.object({
|
|
30788
|
+
creatives: z130.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
30708
30789
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
30709
|
-
portfolioId:
|
|
30790
|
+
portfolioId: z130.string().trim().min(1),
|
|
30710
30791
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
30711
|
-
pageId:
|
|
30792
|
+
pageId: z130.string().trim().min(1),
|
|
30712
30793
|
adAccountId: McpAdAccountId,
|
|
30713
|
-
pixelId:
|
|
30794
|
+
pixelId: z130.string().min(1).optional(),
|
|
30714
30795
|
/** Required unless `campaignConfig.destination` is whatsapp or a creative
|
|
30715
30796
|
* comes from a task (the API then lands on the published service landing
|
|
30716
30797
|
* of its service tasks) — see the superRefine below. Mirrors the API's
|
|
30717
30798
|
* `FacebookCreateCampaignFromTasksBodySchema`. */
|
|
30718
|
-
landingUrl:
|
|
30799
|
+
landingUrl: z130.string().url().optional(),
|
|
30719
30800
|
/** ISO-2 code or resolvable country name — mirrors the API contract
|
|
30720
30801
|
* (CountryIso2Schema), which normalizes names to alpha-2 at validation.
|
|
30721
30802
|
* Omitted: the brand country of its service tasks (the API answers
|
|
30722
30803
|
* META_NO_TARGET_COUNTRY when it has none); "US" for products. */
|
|
30723
30804
|
targetCountry: CountryIso2Schema.optional(),
|
|
30724
|
-
campaignName:
|
|
30805
|
+
campaignName: z130.string().trim().min(1).max(100),
|
|
30725
30806
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
30726
30807
|
});
|
|
30727
30808
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
@@ -30729,7 +30810,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
30729
30810
|
const hasTaskCreative = value.creatives.some((creative) => creative.source.type === "task");
|
|
30730
30811
|
if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl && !hasTaskCreative) {
|
|
30731
30812
|
ctx.addIssue({
|
|
30732
|
-
code:
|
|
30813
|
+
code: z130.ZodIssueCode.custom,
|
|
30733
30814
|
path: [
|
|
30734
30815
|
"landingUrl"
|
|
30735
30816
|
],
|
|
@@ -30738,7 +30819,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
30738
30819
|
}
|
|
30739
30820
|
if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
|
|
30740
30821
|
ctx.addIssue({
|
|
30741
|
-
code:
|
|
30822
|
+
code: z130.ZodIssueCode.custom,
|
|
30742
30823
|
path: [
|
|
30743
30824
|
"campaignConfig",
|
|
30744
30825
|
"destination",
|
|
@@ -30757,16 +30838,16 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
30757
30838
|
}
|
|
30758
30839
|
return value;
|
|
30759
30840
|
});
|
|
30760
|
-
var PresignUploadResponseSchema =
|
|
30761
|
-
uploadUrl:
|
|
30841
|
+
var PresignUploadResponseSchema = z130.object({
|
|
30842
|
+
uploadUrl: z130.string().url().refine((value) => {
|
|
30762
30843
|
const url = new URL(value);
|
|
30763
30844
|
return !url.username && !url.password && url.protocol === "https:";
|
|
30764
30845
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
30765
|
-
publicUrl:
|
|
30846
|
+
publicUrl: z130.string().url().refine((value) => {
|
|
30766
30847
|
const url = new URL(value);
|
|
30767
30848
|
return !url.username && !url.password;
|
|
30768
30849
|
}, "public media URL must not contain embedded credentials"),
|
|
30769
|
-
stagingAssetId:
|
|
30850
|
+
stagingAssetId: z130.string().uuid()
|
|
30770
30851
|
});
|
|
30771
30852
|
async function uploadResolvedMedia(api, media, assetRole) {
|
|
30772
30853
|
if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
|
|
@@ -30792,14 +30873,14 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
|
|
|
30792
30873
|
}
|
|
30793
30874
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
30794
30875
|
var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
30795
|
-
dailyBudgetUsd:
|
|
30796
|
-
dailyBudget:
|
|
30876
|
+
dailyBudgetUsd: z130.number().finite().positive().optional().describe("Legacy USD amount, supported only for USD advertiser accounts. No FX conversion."),
|
|
30877
|
+
dailyBudget: z130.number().finite().positive().optional().describe("Daily budget in the selected advertiser currency major units; pass budgetCurrency together."),
|
|
30797
30878
|
budgetCurrency: TikTokBudgetCurrencySchema.optional(),
|
|
30798
30879
|
destination: TikTokCampaignDestinationSchema.optional()
|
|
30799
30880
|
}).superRefine((value, ctx) => {
|
|
30800
30881
|
if (value.dailyBudgetUsd !== void 0 && (value.dailyBudget !== void 0 || value.budgetCurrency !== void 0)) {
|
|
30801
30882
|
ctx.addIssue({
|
|
30802
|
-
code:
|
|
30883
|
+
code: z130.ZodIssueCode.custom,
|
|
30803
30884
|
path: [
|
|
30804
30885
|
"dailyBudgetUsd"
|
|
30805
30886
|
],
|
|
@@ -30808,7 +30889,7 @@ var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
|
30808
30889
|
}
|
|
30809
30890
|
if (value.dailyBudget !== void 0 !== (value.budgetCurrency !== void 0)) {
|
|
30810
30891
|
ctx.addIssue({
|
|
30811
|
-
code:
|
|
30892
|
+
code: z130.ZodIssueCode.custom,
|
|
30812
30893
|
path: [
|
|
30813
30894
|
"budgetCurrency"
|
|
30814
30895
|
],
|
|
@@ -30816,16 +30897,16 @@ var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
|
30816
30897
|
});
|
|
30817
30898
|
}
|
|
30818
30899
|
});
|
|
30819
|
-
var TikTokInput =
|
|
30820
|
-
taskId:
|
|
30821
|
-
advertiserId:
|
|
30822
|
-
pixelId:
|
|
30823
|
-
landingUrl:
|
|
30824
|
-
angleIndexes:
|
|
30900
|
+
var TikTokInput = z130.object({
|
|
30901
|
+
taskId: z130.string().min(1),
|
|
30902
|
+
advertiserId: z130.string().min(1),
|
|
30903
|
+
pixelId: z130.string().min(1).optional(),
|
|
30904
|
+
landingUrl: z130.string().url().optional(),
|
|
30905
|
+
angleIndexes: z130.array(z130.number().int().nonnegative()).optional(),
|
|
30825
30906
|
campaignConfig: TikTokMcpCampaignConfig.optional(),
|
|
30826
30907
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
30827
30908
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
30828
|
-
primaryText:
|
|
30909
|
+
primaryText: z130.string().trim().min(1).max(500).optional()
|
|
30829
30910
|
});
|
|
30830
30911
|
function registerPublishTools(server, api, opts = {}) {
|
|
30831
30912
|
const transport = opts.transport ?? "http";
|
|
@@ -31184,39 +31265,39 @@ __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
|
31184
31265
|
|
|
31185
31266
|
// src/tools/meta-upload.ts
|
|
31186
31267
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
31187
|
-
import { z as
|
|
31268
|
+
import { z as z131 } from "zod";
|
|
31188
31269
|
var requestMediaUploadGuidance = getProductMakerCapabilityModelGuidance("request_media_upload");
|
|
31189
|
-
var UploadSessionInputBase =
|
|
31190
|
-
mediaKind:
|
|
31270
|
+
var UploadSessionInputBase = z131.object({
|
|
31271
|
+
mediaKind: z131.enum([
|
|
31191
31272
|
"image",
|
|
31192
31273
|
"video",
|
|
31193
31274
|
"mixed"
|
|
31194
31275
|
]).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."),
|
|
31195
|
-
assetRole:
|
|
31276
|
+
assetRole: z131.enum([
|
|
31196
31277
|
"media",
|
|
31197
31278
|
"thumbnail"
|
|
31198
31279
|
]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail."),
|
|
31199
|
-
uploadPurpose:
|
|
31280
|
+
uploadPurpose: z131.enum([
|
|
31200
31281
|
"product_reference",
|
|
31201
31282
|
"creative",
|
|
31202
31283
|
"generic"
|
|
31203
31284
|
]).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."),
|
|
31204
|
-
maxFiles:
|
|
31285
|
+
maxFiles: z131.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
|
|
31205
31286
|
});
|
|
31206
|
-
var OpenAiFileReference =
|
|
31207
|
-
download_url:
|
|
31208
|
-
file_id:
|
|
31209
|
-
mime_type:
|
|
31210
|
-
file_name:
|
|
31287
|
+
var OpenAiFileReference = z131.object({
|
|
31288
|
+
download_url: z131.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."),
|
|
31289
|
+
file_id: z131.string().min(1),
|
|
31290
|
+
mime_type: z131.string().min(1).optional(),
|
|
31291
|
+
file_name: z131.string().min(1).optional()
|
|
31211
31292
|
});
|
|
31212
31293
|
var UploadSessionToolInputShape = {
|
|
31213
31294
|
...UploadSessionInputBase.shape,
|
|
31214
|
-
files:
|
|
31295
|
+
files: z131.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.")
|
|
31215
31296
|
};
|
|
31216
|
-
var UploadSessionToolInput =
|
|
31297
|
+
var UploadSessionToolInput = z131.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
|
|
31217
31298
|
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
31218
31299
|
ctx.addIssue({
|
|
31219
|
-
code:
|
|
31300
|
+
code: z131.ZodIssueCode.custom,
|
|
31220
31301
|
path: [
|
|
31221
31302
|
"mediaKind"
|
|
31222
31303
|
],
|
|
@@ -31225,7 +31306,7 @@ var UploadSessionToolInput = z130.object(UploadSessionToolInputShape).superRefin
|
|
|
31225
31306
|
}
|
|
31226
31307
|
if (value.mediaKind === "video" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
|
|
31227
31308
|
ctx.addIssue({
|
|
31228
|
-
code:
|
|
31309
|
+
code: z131.ZodIssueCode.custom,
|
|
31229
31310
|
path: [
|
|
31230
31311
|
"maxFiles"
|
|
31231
31312
|
],
|
|
@@ -31234,7 +31315,7 @@ var UploadSessionToolInput = z130.object(UploadSessionToolInputShape).superRefin
|
|
|
31234
31315
|
}
|
|
31235
31316
|
if (value.mediaKind === "image" && (value.maxFiles ?? 1) > 5) {
|
|
31236
31317
|
ctx.addIssue({
|
|
31237
|
-
code:
|
|
31318
|
+
code: z131.ZodIssueCode.custom,
|
|
31238
31319
|
path: [
|
|
31239
31320
|
"maxFiles"
|
|
31240
31321
|
],
|
|
@@ -31242,38 +31323,38 @@ var UploadSessionToolInput = z130.object(UploadSessionToolInputShape).superRefin
|
|
|
31242
31323
|
});
|
|
31243
31324
|
}
|
|
31244
31325
|
});
|
|
31245
|
-
var UploadSessionResponse =
|
|
31246
|
-
sessionId:
|
|
31247
|
-
uploadPageUrl:
|
|
31248
|
-
mediaKind:
|
|
31326
|
+
var UploadSessionResponse = z131.object({
|
|
31327
|
+
sessionId: z131.string().uuid(),
|
|
31328
|
+
uploadPageUrl: z131.string().url(),
|
|
31329
|
+
mediaKind: z131.enum([
|
|
31249
31330
|
"image",
|
|
31250
31331
|
"video",
|
|
31251
31332
|
"mixed"
|
|
31252
31333
|
]),
|
|
31253
|
-
assetRole:
|
|
31334
|
+
assetRole: z131.enum([
|
|
31254
31335
|
"media",
|
|
31255
31336
|
"thumbnail"
|
|
31256
31337
|
]),
|
|
31257
|
-
maxFiles:
|
|
31258
|
-
expiresAt:
|
|
31338
|
+
maxFiles: z131.number().int().positive(),
|
|
31339
|
+
expiresAt: z131.string().datetime()
|
|
31259
31340
|
});
|
|
31260
|
-
var UploadStatusInput =
|
|
31261
|
-
sessionId:
|
|
31341
|
+
var UploadStatusInput = z131.object({
|
|
31342
|
+
sessionId: z131.string().uuid().describe("The sessionId returned by request_media_upload.")
|
|
31262
31343
|
});
|
|
31263
|
-
var UniversalUploadStatusResponse =
|
|
31264
|
-
|
|
31265
|
-
status:
|
|
31266
|
-
assets:
|
|
31344
|
+
var UniversalUploadStatusResponse = z131.discriminatedUnion("status", [
|
|
31345
|
+
z131.object({
|
|
31346
|
+
status: z131.literal("pending"),
|
|
31347
|
+
assets: z131.array(z131.never()).length(0)
|
|
31267
31348
|
}),
|
|
31268
|
-
|
|
31269
|
-
status:
|
|
31270
|
-
assets:
|
|
31271
|
-
assetId:
|
|
31272
|
-
mediaKind:
|
|
31349
|
+
z131.object({
|
|
31350
|
+
status: z131.literal("uploaded"),
|
|
31351
|
+
assets: z131.array(z131.object({
|
|
31352
|
+
assetId: z131.string().uuid(),
|
|
31353
|
+
mediaKind: z131.enum([
|
|
31273
31354
|
"image",
|
|
31274
31355
|
"video"
|
|
31275
31356
|
]),
|
|
31276
|
-
assetRole:
|
|
31357
|
+
assetRole: z131.enum([
|
|
31277
31358
|
"media",
|
|
31278
31359
|
"thumbnail"
|
|
31279
31360
|
])
|
|
@@ -31281,13 +31362,13 @@ var UniversalUploadStatusResponse = z130.discriminatedUnion("status", [
|
|
|
31281
31362
|
})
|
|
31282
31363
|
]);
|
|
31283
31364
|
var UniversalUploadStructuredResponse = UploadSessionResponse.extend({
|
|
31284
|
-
uploadPurpose:
|
|
31365
|
+
uploadPurpose: z131.enum([
|
|
31285
31366
|
"product_reference",
|
|
31286
31367
|
"creative",
|
|
31287
31368
|
"generic"
|
|
31288
31369
|
]),
|
|
31289
|
-
checkToolName:
|
|
31290
|
-
resolveEndpoint:
|
|
31370
|
+
checkToolName: z131.literal("check_media_upload"),
|
|
31371
|
+
resolveEndpoint: z131.string().url()
|
|
31291
31372
|
});
|
|
31292
31373
|
function contextualizeUploadPageUrl(uploadPageUrl, uploadPurpose) {
|
|
31293
31374
|
const url = new URL(uploadPageUrl);
|
|
@@ -31378,15 +31459,15 @@ function registerMediaUploadTools(server, api) {
|
|
|
31378
31459
|
__name(registerMediaUploadTools, "registerMediaUploadTools");
|
|
31379
31460
|
|
|
31380
31461
|
// src/tools/campaigns.ts
|
|
31381
|
-
import { z as
|
|
31462
|
+
import { z as z132 } from "zod";
|
|
31382
31463
|
var getMetaCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_meta_campaign_status");
|
|
31383
31464
|
var getTikTokCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_tiktok_campaign_status");
|
|
31384
|
-
var MetaStatusInput =
|
|
31385
|
-
campaignId:
|
|
31465
|
+
var MetaStatusInput = z132.object({
|
|
31466
|
+
campaignId: z132.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
31386
31467
|
});
|
|
31387
|
-
var TikTokStatusInput =
|
|
31388
|
-
campaignId:
|
|
31389
|
-
advertiserId:
|
|
31468
|
+
var TikTokStatusInput = z132.object({
|
|
31469
|
+
campaignId: z132.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
31470
|
+
advertiserId: z132.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
31390
31471
|
});
|
|
31391
31472
|
function registerCampaignTools(server, api) {
|
|
31392
31473
|
server.registerTool("get_meta_campaign_status", {
|
|
@@ -31491,10 +31572,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
31491
31572
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
31492
31573
|
|
|
31493
31574
|
// ../../packages/product-economics/src/product-costing.ts
|
|
31494
|
-
import { z as
|
|
31575
|
+
import { z as z134 } from "zod";
|
|
31495
31576
|
|
|
31496
31577
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
31497
|
-
import { z as
|
|
31578
|
+
import { z as z133 } from "zod";
|
|
31498
31579
|
|
|
31499
31580
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
31500
31581
|
var product_costing_shipping_references_default = {
|
|
@@ -31558,21 +31639,21 @@ var product_costing_shipping_references_default = {
|
|
|
31558
31639
|
};
|
|
31559
31640
|
|
|
31560
31641
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
31561
|
-
var ShippingReferenceSchema =
|
|
31562
|
-
reference:
|
|
31563
|
-
min:
|
|
31564
|
-
max:
|
|
31565
|
-
updatedAt:
|
|
31566
|
-
confidence:
|
|
31642
|
+
var ShippingReferenceSchema = z133.object({
|
|
31643
|
+
reference: z133.number().positive(),
|
|
31644
|
+
min: z133.number().nonnegative(),
|
|
31645
|
+
max: z133.number().positive(),
|
|
31646
|
+
updatedAt: z133.string().date(),
|
|
31647
|
+
confidence: z133.enum([
|
|
31567
31648
|
"high",
|
|
31568
31649
|
"medium",
|
|
31569
31650
|
"medium-low"
|
|
31570
31651
|
]),
|
|
31571
|
-
sources:
|
|
31652
|
+
sources: z133.array(z133.string().url().startsWith("https://")).min(1).max(3)
|
|
31572
31653
|
}).superRefine(({ reference, min, max }, context) => {
|
|
31573
31654
|
if (min > reference || reference > max) {
|
|
31574
31655
|
context.addIssue({
|
|
31575
|
-
code:
|
|
31656
|
+
code: z133.ZodIssueCode.custom,
|
|
31576
31657
|
message: "shipping_reference_outside_range",
|
|
31577
31658
|
path: [
|
|
31578
31659
|
"reference"
|
|
@@ -31580,7 +31661,7 @@ var ShippingReferenceSchema = z132.object({
|
|
|
31580
31661
|
});
|
|
31581
31662
|
}
|
|
31582
31663
|
});
|
|
31583
|
-
var SHIPPING_REFERENCES =
|
|
31664
|
+
var SHIPPING_REFERENCES = z133.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
31584
31665
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
31585
31666
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
31586
31667
|
"MX",
|
|
@@ -31762,18 +31843,18 @@ function getProductCostingCountryName(country) {
|
|
|
31762
31843
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
31763
31844
|
|
|
31764
31845
|
// ../../packages/product-economics/src/product-costing.ts
|
|
31765
|
-
var ProductCostingInputSchema =
|
|
31766
|
-
country:
|
|
31767
|
-
supplierCost:
|
|
31768
|
-
shippingCost:
|
|
31769
|
-
returnRate:
|
|
31770
|
-
cancellationRate:
|
|
31771
|
-
adsManagerCpa:
|
|
31772
|
-
desiredMargin:
|
|
31846
|
+
var ProductCostingInputSchema = z134.object({
|
|
31847
|
+
country: z134.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
31848
|
+
supplierCost: z134.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
31849
|
+
shippingCost: z134.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
31850
|
+
returnRate: z134.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
31851
|
+
cancellationRate: z134.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
31852
|
+
adsManagerCpa: z134.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
31853
|
+
desiredMargin: z134.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
31773
31854
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
31774
31855
|
if (returnRate + cancellationRate >= 1) {
|
|
31775
31856
|
context.addIssue({
|
|
31776
|
-
code:
|
|
31857
|
+
code: z134.ZodIssueCode.custom,
|
|
31777
31858
|
message: "delivery_rate_positive",
|
|
31778
31859
|
path: [
|
|
31779
31860
|
"cancellationRate"
|
|
@@ -31870,15 +31951,15 @@ function calculateProductCosting(rawInput) {
|
|
|
31870
31951
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
31871
31952
|
|
|
31872
31953
|
// src/tools/product-costing.ts
|
|
31873
|
-
import { z as
|
|
31954
|
+
import { z as z135 } from "zod";
|
|
31874
31955
|
var costProductGuidance = getProductMakerCapabilityModelGuidance("cost_product");
|
|
31875
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
31876
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
31877
|
-
var ProductCostingToolInput =
|
|
31878
|
-
country:
|
|
31956
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z135.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
31957
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z135.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
31958
|
+
var ProductCostingToolInput = z135.object({
|
|
31959
|
+
country: z135.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
31879
31960
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
31880
31961
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
31881
|
-
supplierCost:
|
|
31962
|
+
supplierCost: z135.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."),
|
|
31882
31963
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
31883
31964
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
31884
31965
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -32041,7 +32122,7 @@ function formatPercent(value) {
|
|
|
32041
32122
|
__name(formatPercent, "formatPercent");
|
|
32042
32123
|
|
|
32043
32124
|
// src/tools/product-research.ts
|
|
32044
|
-
import { z as
|
|
32125
|
+
import { z as z136 } from "zod";
|
|
32045
32126
|
|
|
32046
32127
|
// src/tools/product-research-idempotency.ts
|
|
32047
32128
|
var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
|
|
@@ -32412,17 +32493,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
32412
32493
|
]);
|
|
32413
32494
|
var POLL_INTERVAL_MS = 5e3;
|
|
32414
32495
|
var MAX_WAIT_SECONDS = 120;
|
|
32415
|
-
var FindInput =
|
|
32496
|
+
var FindInput = z136.object({
|
|
32416
32497
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
32417
32498
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
32418
|
-
confirmed:
|
|
32419
|
-
waitSeconds:
|
|
32499
|
+
confirmed: z136.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
|
|
32500
|
+
waitSeconds: z136.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
|
|
32420
32501
|
});
|
|
32421
|
-
var GetInput =
|
|
32422
|
-
runId:
|
|
32502
|
+
var GetInput = z136.object({
|
|
32503
|
+
runId: z136.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
32423
32504
|
});
|
|
32424
|
-
var ListInput =
|
|
32425
|
-
limit:
|
|
32505
|
+
var ListInput = z136.object({
|
|
32506
|
+
limit: z136.number().int().min(1).max(20).default(10)
|
|
32426
32507
|
});
|
|
32427
32508
|
function textResult(text2) {
|
|
32428
32509
|
return {
|
|
@@ -32561,17 +32642,17 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
32561
32642
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
32562
32643
|
|
|
32563
32644
|
// src/tools/dropshipping-projects.ts
|
|
32564
|
-
import { z as
|
|
32645
|
+
import { z as z137 } from "zod";
|
|
32565
32646
|
var startDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("start_dropshipping_project");
|
|
32566
32647
|
var costDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("cost_dropshipping_project");
|
|
32567
32648
|
var guideDropshippingLaunchGuidance = getProductMakerCapabilityModelGuidance("guide_dropshipping_launch");
|
|
32568
|
-
var ProjectId =
|
|
32569
|
-
var DropshippingProjectStatus =
|
|
32649
|
+
var ProjectId = z137.string().uuid();
|
|
32650
|
+
var DropshippingProjectStatus = z137.enum([
|
|
32570
32651
|
"active",
|
|
32571
32652
|
"completed",
|
|
32572
32653
|
"archived"
|
|
32573
32654
|
]);
|
|
32574
|
-
var DropshippingProjectPhase =
|
|
32655
|
+
var DropshippingProjectPhase = z137.enum([
|
|
32575
32656
|
"research",
|
|
32576
32657
|
"costing",
|
|
32577
32658
|
"media",
|
|
@@ -32580,21 +32661,21 @@ var DropshippingProjectPhase = z136.enum([
|
|
|
32580
32661
|
"advertising",
|
|
32581
32662
|
"complete"
|
|
32582
32663
|
]);
|
|
32583
|
-
var DropshippingRewardMilestone =
|
|
32664
|
+
var DropshippingRewardMilestone = z137.enum([
|
|
32584
32665
|
"product_selected",
|
|
32585
32666
|
"costing_saved",
|
|
32586
32667
|
"product_media_ready",
|
|
32587
32668
|
"creatives_completed",
|
|
32588
32669
|
"campaign_created"
|
|
32589
32670
|
]);
|
|
32590
|
-
var TargetCountry =
|
|
32671
|
+
var TargetCountry = z137.enum([
|
|
32591
32672
|
"CO",
|
|
32592
32673
|
"MX",
|
|
32593
32674
|
"PE",
|
|
32594
32675
|
"EC",
|
|
32595
32676
|
"CL"
|
|
32596
32677
|
]);
|
|
32597
|
-
var ResearchCategory =
|
|
32678
|
+
var ResearchCategory = z137.enum([
|
|
32598
32679
|
"auto",
|
|
32599
32680
|
"automotive",
|
|
32600
32681
|
"kitchen",
|
|
@@ -32609,7 +32690,7 @@ var ResearchCategory = z136.enum([
|
|
|
32609
32690
|
"garden",
|
|
32610
32691
|
"cleaning"
|
|
32611
32692
|
]);
|
|
32612
|
-
var HttpUrl3 =
|
|
32693
|
+
var HttpUrl3 = z137.string().url().max(2e3).refine((value) => {
|
|
32613
32694
|
try {
|
|
32614
32695
|
return [
|
|
32615
32696
|
"http:",
|
|
@@ -32619,45 +32700,45 @@ var HttpUrl3 = z136.string().url().max(2e3).refine((value) => {
|
|
|
32619
32700
|
return false;
|
|
32620
32701
|
}
|
|
32621
32702
|
}, "URL must use HTTP(S)");
|
|
32622
|
-
var ResearchCandidateOrigin =
|
|
32623
|
-
type:
|
|
32703
|
+
var ResearchCandidateOrigin = z137.object({
|
|
32704
|
+
type: z137.literal("research_candidate"),
|
|
32624
32705
|
runId: ProjectId,
|
|
32625
|
-
candidateSourceId:
|
|
32706
|
+
candidateSourceId: z137.string().min(1).max(160)
|
|
32626
32707
|
});
|
|
32627
|
-
var TaskOrigin =
|
|
32628
|
-
type:
|
|
32708
|
+
var TaskOrigin = z137.object({
|
|
32709
|
+
type: z137.literal("task"),
|
|
32629
32710
|
taskId: ProjectId
|
|
32630
32711
|
});
|
|
32631
|
-
var ManualOrigin =
|
|
32632
|
-
type:
|
|
32712
|
+
var ManualOrigin = z137.object({
|
|
32713
|
+
type: z137.literal("manual"),
|
|
32633
32714
|
clientRequestId: ProjectId,
|
|
32634
|
-
country:
|
|
32715
|
+
country: z137.string().min(2).max(3).optional()
|
|
32635
32716
|
});
|
|
32636
|
-
var StartProjectInput =
|
|
32637
|
-
origin:
|
|
32717
|
+
var StartProjectInput = z137.object({
|
|
32718
|
+
origin: z137.discriminatedUnion("type", [
|
|
32638
32719
|
ResearchCandidateOrigin,
|
|
32639
32720
|
TaskOrigin,
|
|
32640
32721
|
ManualOrigin
|
|
32641
32722
|
]),
|
|
32642
|
-
productName:
|
|
32723
|
+
productName: z137.string().trim().min(1).max(240).optional(),
|
|
32643
32724
|
productUrl: HttpUrl3.optional(),
|
|
32644
|
-
country:
|
|
32645
|
-
languageCode:
|
|
32725
|
+
country: z137.string().min(2).max(3).optional(),
|
|
32726
|
+
languageCode: z137.string().min(2).max(16).optional()
|
|
32646
32727
|
}).strict();
|
|
32647
|
-
var CostProjectInput =
|
|
32728
|
+
var CostProjectInput = z137.object({
|
|
32648
32729
|
projectId: ProjectId,
|
|
32649
|
-
version:
|
|
32650
|
-
country:
|
|
32730
|
+
version: z137.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
|
|
32731
|
+
country: z137.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
32651
32732
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
32652
32733
|
}).describe("C\xF3digo ISO-2 del pa\xEDs del proyecto. Usa el `targetCountry` que devuelve el proyecto."),
|
|
32653
|
-
supplierCost:
|
|
32654
|
-
shippingCost:
|
|
32655
|
-
returnRate:
|
|
32656
|
-
cancellationRate:
|
|
32657
|
-
adsManagerCpa:
|
|
32658
|
-
desiredMargin:
|
|
32659
|
-
});
|
|
32660
|
-
var GuideInput =
|
|
32734
|
+
supplierCost: z137.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."),
|
|
32735
|
+
shippingCost: z137.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
|
|
32736
|
+
returnRate: z137.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."),
|
|
32737
|
+
cancellationRate: z137.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."),
|
|
32738
|
+
adsManagerCpa: z137.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
|
|
32739
|
+
desiredMargin: z137.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
|
|
32740
|
+
});
|
|
32741
|
+
var GuideInput = z137.object({
|
|
32661
32742
|
projectId: ProjectId.optional()
|
|
32662
32743
|
}).strict();
|
|
32663
32744
|
var NEXT_ACTION_KINDS = [
|
|
@@ -32675,29 +32756,29 @@ var NEXT_ACTION_KINDS = [
|
|
|
32675
32756
|
"repair_meta_campaign",
|
|
32676
32757
|
"review_paused_campaign"
|
|
32677
32758
|
];
|
|
32678
|
-
var DropshippingProjectNextAction =
|
|
32679
|
-
kind:
|
|
32680
|
-
href:
|
|
32759
|
+
var DropshippingProjectNextAction = z137.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind) => z137.object({
|
|
32760
|
+
kind: z137.literal(kind),
|
|
32761
|
+
href: z137.string().min(1)
|
|
32681
32762
|
})));
|
|
32682
|
-
var CostingInputSchema =
|
|
32683
|
-
country:
|
|
32684
|
-
supplierCost:
|
|
32685
|
-
shippingCost:
|
|
32686
|
-
returnRate:
|
|
32687
|
-
cancellationRate:
|
|
32688
|
-
adsManagerCpa:
|
|
32689
|
-
desiredMargin:
|
|
32763
|
+
var CostingInputSchema = z137.object({
|
|
32764
|
+
country: z137.string().min(2).max(3),
|
|
32765
|
+
supplierCost: z137.number().finite().positive(),
|
|
32766
|
+
shippingCost: z137.number().finite().nonnegative(),
|
|
32767
|
+
returnRate: z137.number().finite().min(0).lt(1),
|
|
32768
|
+
cancellationRate: z137.number().finite().min(0).lt(1),
|
|
32769
|
+
adsManagerCpa: z137.number().finite().nonnegative(),
|
|
32770
|
+
desiredMargin: z137.number().finite().min(0).lt(1)
|
|
32690
32771
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
32691
32772
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
32692
|
-
code:
|
|
32773
|
+
code: z137.ZodIssueCode.custom,
|
|
32693
32774
|
path: [
|
|
32694
32775
|
"cancellationRate"
|
|
32695
32776
|
]
|
|
32696
32777
|
});
|
|
32697
32778
|
});
|
|
32698
|
-
var FiniteCostingNumber =
|
|
32699
|
-
var CostingResultSchema =
|
|
32700
|
-
country:
|
|
32779
|
+
var FiniteCostingNumber = z137.number().finite();
|
|
32780
|
+
var CostingResultSchema = z137.object({
|
|
32781
|
+
country: z137.string().min(2).max(3),
|
|
32701
32782
|
deliveryRate: FiniteCostingNumber,
|
|
32702
32783
|
adjustedShippingCost: FiniteCostingNumber,
|
|
32703
32784
|
adjustedCpa: FiniteCostingNumber,
|
|
@@ -32710,45 +32791,45 @@ var CostingResultSchema = z136.object({
|
|
|
32710
32791
|
estimatedProfit: FiniteCostingNumber,
|
|
32711
32792
|
actualMargin: FiniteCostingNumber,
|
|
32712
32793
|
breakEvenAdsManagerCpa: FiniteCostingNumber,
|
|
32713
|
-
wasPriceAutoAdjusted:
|
|
32714
|
-
profitabilityStatus:
|
|
32715
|
-
});
|
|
32716
|
-
var RewardProgressSchema =
|
|
32717
|
-
campaignKey:
|
|
32718
|
-
totalCredits:
|
|
32719
|
-
earnedCredits:
|
|
32720
|
-
milestones:
|
|
32794
|
+
wasPriceAutoAdjusted: z137.boolean(),
|
|
32795
|
+
profitabilityStatus: z137.literal("healthy")
|
|
32796
|
+
});
|
|
32797
|
+
var RewardProgressSchema = z137.object({
|
|
32798
|
+
campaignKey: z137.literal("dropshipping-launch-v1"),
|
|
32799
|
+
totalCredits: z137.number().int().positive(),
|
|
32800
|
+
earnedCredits: z137.number().int().nonnegative(),
|
|
32801
|
+
milestones: z137.array(z137.object({
|
|
32721
32802
|
milestone: DropshippingRewardMilestone,
|
|
32722
|
-
credits:
|
|
32723
|
-
available:
|
|
32724
|
-
earned:
|
|
32803
|
+
credits: z137.number().int().positive(),
|
|
32804
|
+
available: z137.boolean().optional(),
|
|
32805
|
+
earned: z137.boolean(),
|
|
32725
32806
|
grantId: ProjectId.nullable(),
|
|
32726
|
-
grantedAt:
|
|
32807
|
+
grantedAt: z137.string().datetime().nullable()
|
|
32727
32808
|
})).length(DropshippingRewardMilestone.options.length)
|
|
32728
32809
|
});
|
|
32729
|
-
var ProjectResponseSchema =
|
|
32810
|
+
var ProjectResponseSchema = z137.object({
|
|
32730
32811
|
id: ProjectId,
|
|
32731
32812
|
taskId: ProjectId.nullable(),
|
|
32732
32813
|
status: DropshippingProjectStatus,
|
|
32733
|
-
originKey:
|
|
32814
|
+
originKey: z137.string().min(1),
|
|
32734
32815
|
researchRunId: ProjectId.nullable(),
|
|
32735
|
-
candidateSourceId:
|
|
32736
|
-
productName:
|
|
32816
|
+
candidateSourceId: z137.string().nullable(),
|
|
32817
|
+
productName: z137.string().nullable(),
|
|
32737
32818
|
productUrl: HttpUrl3.nullable(),
|
|
32738
32819
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
32739
|
-
targetCountry:
|
|
32740
|
-
languageCode:
|
|
32741
|
-
hasReferenceImage:
|
|
32742
|
-
hasWinnerReferenceImage:
|
|
32820
|
+
targetCountry: z137.string().nullable(),
|
|
32821
|
+
languageCode: z137.string().nullable(),
|
|
32822
|
+
hasReferenceImage: z137.boolean(),
|
|
32823
|
+
hasWinnerReferenceImage: z137.boolean(),
|
|
32743
32824
|
costingInput: CostingInputSchema.nullable(),
|
|
32744
32825
|
costingResult: CostingResultSchema.nullable(),
|
|
32745
|
-
costingVersion:
|
|
32746
|
-
version:
|
|
32826
|
+
costingVersion: z137.number().int().positive().nullable(),
|
|
32827
|
+
version: z137.number().int().positive(),
|
|
32747
32828
|
phase: DropshippingProjectPhase,
|
|
32748
32829
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
32749
32830
|
rewards: RewardProgressSchema,
|
|
32750
|
-
createdAt:
|
|
32751
|
-
updatedAt:
|
|
32831
|
+
createdAt: z137.string().datetime(),
|
|
32832
|
+
updatedAt: z137.string().datetime()
|
|
32752
32833
|
}).passthrough();
|
|
32753
32834
|
var NEXT_ACTION_GUIDANCE = {
|
|
32754
32835
|
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.",
|
|
@@ -32868,58 +32949,58 @@ function registerDropshippingProjectTools(server, api) {
|
|
|
32868
32949
|
__name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
|
|
32869
32950
|
|
|
32870
32951
|
// src/tools/daily-control.ts
|
|
32871
|
-
import { z as
|
|
32952
|
+
import { z as z138 } from "zod";
|
|
32872
32953
|
var getDailyControlGuidance = getProductMakerCapabilityModelGuidance("get_daily_control");
|
|
32873
32954
|
var linkCampaignToProjectGuidance = getProductMakerCapabilityModelGuidance("link_campaign_to_project");
|
|
32874
32955
|
var reportDailyReturnsGuidance = getProductMakerCapabilityModelGuidance("report_daily_returns");
|
|
32875
|
-
var ProviderCampaignIdentifier =
|
|
32876
|
-
var DateOnly =
|
|
32877
|
-
var DailyControlInput =
|
|
32956
|
+
var ProviderCampaignIdentifier = z138.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
|
|
32957
|
+
var DateOnly = z138.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
32958
|
+
var DailyControlInput = z138.object({
|
|
32878
32959
|
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."),
|
|
32879
|
-
days:
|
|
32880
|
-
projectId:
|
|
32960
|
+
days: z138.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
|
|
32961
|
+
projectId: z138.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
|
|
32881
32962
|
}).strict();
|
|
32882
|
-
var LinkCampaignInput =
|
|
32883
|
-
projectId:
|
|
32884
|
-
provider:
|
|
32963
|
+
var LinkCampaignInput = z138.object({
|
|
32964
|
+
projectId: z138.string().uuid(),
|
|
32965
|
+
provider: z138.enum([
|
|
32885
32966
|
"meta",
|
|
32886
32967
|
"tiktok"
|
|
32887
32968
|
]),
|
|
32888
32969
|
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."),
|
|
32889
32970
|
campaignId: ProviderCampaignIdentifier
|
|
32890
32971
|
}).strict();
|
|
32891
|
-
var ReportReturnsInput =
|
|
32892
|
-
projectId:
|
|
32972
|
+
var ReportReturnsInput = z138.object({
|
|
32973
|
+
projectId: z138.string().uuid(),
|
|
32893
32974
|
date: DateOnly,
|
|
32894
|
-
returnedOrders:
|
|
32895
|
-
note:
|
|
32975
|
+
returnedOrders: z138.number().int().min(0).max(1e5),
|
|
32976
|
+
note: z138.string().trim().max(280).optional()
|
|
32896
32977
|
}).strict();
|
|
32897
|
-
var DailyControlStatusMirror = DailyControlStatusEnum.or(
|
|
32898
|
-
var DailyControlLightMirror = DailyControlLightEnum.or(
|
|
32899
|
-
var DailyControlSourceMirror = DailyControlSourceEnum.or(
|
|
32900
|
-
var DailyControlVerdictMirror = DailyControlVerdictEnum.or(
|
|
32901
|
-
var DailyControlDayMirror =
|
|
32902
|
-
date:
|
|
32903
|
-
returnedOrders:
|
|
32978
|
+
var DailyControlStatusMirror = DailyControlStatusEnum.or(z138.string());
|
|
32979
|
+
var DailyControlLightMirror = DailyControlLightEnum.or(z138.string());
|
|
32980
|
+
var DailyControlSourceMirror = DailyControlSourceEnum.or(z138.string());
|
|
32981
|
+
var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z138.string());
|
|
32982
|
+
var DailyControlDayMirror = z138.object({
|
|
32983
|
+
date: z138.string(),
|
|
32984
|
+
returnedOrders: z138.number(),
|
|
32904
32985
|
returnedSource: DailyControlSourceMirror,
|
|
32905
32986
|
cancelledSource: DailyControlSourceMirror,
|
|
32906
|
-
hasEstimates:
|
|
32987
|
+
hasEstimates: z138.boolean(),
|
|
32907
32988
|
light: DailyControlLightMirror
|
|
32908
32989
|
}).passthrough();
|
|
32909
|
-
var DailyControlProductMirror =
|
|
32910
|
-
projectId:
|
|
32911
|
-
productName:
|
|
32990
|
+
var DailyControlProductMirror = z138.object({
|
|
32991
|
+
projectId: z138.string().uuid(),
|
|
32992
|
+
productName: z138.string().nullable(),
|
|
32912
32993
|
status: DailyControlStatusMirror,
|
|
32913
|
-
statusMessage:
|
|
32914
|
-
days:
|
|
32994
|
+
statusMessage: z138.string().nullable(),
|
|
32995
|
+
days: z138.array(DailyControlDayMirror),
|
|
32915
32996
|
verdict: DailyControlVerdictMirror.nullable(),
|
|
32916
|
-
rationale:
|
|
32997
|
+
rationale: z138.string().nullable()
|
|
32917
32998
|
}).passthrough();
|
|
32918
|
-
var DailyControlResponseMirror =
|
|
32919
|
-
since:
|
|
32920
|
-
until:
|
|
32921
|
-
products:
|
|
32922
|
-
archivedProjectSpend:
|
|
32999
|
+
var DailyControlResponseMirror = z138.object({
|
|
33000
|
+
since: z138.string(),
|
|
33001
|
+
until: z138.string(),
|
|
33002
|
+
products: z138.array(DailyControlProductMirror),
|
|
33003
|
+
archivedProjectSpend: z138.array(z138.unknown())
|
|
32923
33004
|
}).passthrough();
|
|
32924
33005
|
var MCP_WARNINGS_KEY = "_mcpDailyControlWarnings";
|
|
32925
33006
|
function withWarnings(response) {
|
|
@@ -32994,7 +33075,7 @@ __name(registerDailyControlTools, "registerDailyControlTools");
|
|
|
32994
33075
|
// src/tools/product-control.ts
|
|
32995
33076
|
import { readFile, stat } from "fs/promises";
|
|
32996
33077
|
import { basename as basename2, isAbsolute, resolve as resolve3 } from "path";
|
|
32997
|
-
import { z as
|
|
33078
|
+
import { z as z139 } from "zod";
|
|
32998
33079
|
var getProductControlGuidance = getProductMakerCapabilityModelGuidance("get_product_control");
|
|
32999
33080
|
var listImportableProductsGuidance = getProductMakerCapabilityModelGuidance("list_importable_products");
|
|
33000
33081
|
var importProductControlsGuidance = getProductMakerCapabilityModelGuidance("import_product_controls");
|
|
@@ -33007,36 +33088,36 @@ var createOperationGuidance = getProductMakerCapabilityModelGuidance("create_ope
|
|
|
33007
33088
|
var mergeProductsGuidance = getProductMakerCapabilityModelGuidance("merge_products");
|
|
33008
33089
|
var syncShopifyOrdersGuidance = getProductMakerCapabilityModelGuidance("sync_shopify_orders");
|
|
33009
33090
|
var importOrdersFileGuidance = getProductMakerCapabilityModelGuidance("import_orders_file");
|
|
33010
|
-
var DateOnly2 =
|
|
33091
|
+
var DateOnly2 = z139.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
33011
33092
|
var IMPORT_BATCH_MAX2 = 50;
|
|
33012
|
-
var ProductControlListInput =
|
|
33013
|
-
days:
|
|
33093
|
+
var ProductControlListInput = z139.object({
|
|
33094
|
+
days: z139.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
|
|
33014
33095
|
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."),
|
|
33015
|
-
shopDomain:
|
|
33096
|
+
shopDomain: z139.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
|
|
33016
33097
|
}).strict();
|
|
33017
|
-
var ImportableProductsInput =
|
|
33018
|
-
q:
|
|
33019
|
-
after:
|
|
33020
|
-
shopDomain:
|
|
33098
|
+
var ImportableProductsInput = z139.object({
|
|
33099
|
+
q: z139.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
|
|
33100
|
+
after: z139.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
|
|
33101
|
+
shopDomain: z139.string().min(1).optional()
|
|
33021
33102
|
}).strict();
|
|
33022
|
-
var ImportProductControlsInput =
|
|
33023
|
-
shopifyProductIds:
|
|
33103
|
+
var ImportProductControlsInput = z139.object({
|
|
33104
|
+
shopifyProductIds: z139.array(z139.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."),
|
|
33024
33105
|
// Obligatorio, como en el endpoint: con dos tiendas conectadas, importar
|
|
33025
33106
|
// sin decir a cuál archivaría el producto bajo la tienda equivocada.
|
|
33026
|
-
shopDomain:
|
|
33107
|
+
shopDomain: z139.string().min(1)
|
|
33027
33108
|
}).strict();
|
|
33028
|
-
var SetProductCostingInput =
|
|
33029
|
-
shopDomain:
|
|
33030
|
-
shopifyProductId:
|
|
33031
|
-
targetCountry:
|
|
33032
|
-
supplierCost:
|
|
33033
|
-
shippingCost:
|
|
33034
|
-
returnRate:
|
|
33035
|
-
cancellationRate:
|
|
33109
|
+
var SetProductCostingInput = z139.object({
|
|
33110
|
+
shopDomain: z139.string().min(1),
|
|
33111
|
+
shopifyProductId: z139.string().min(1).max(64),
|
|
33112
|
+
targetCountry: z139.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
|
|
33113
|
+
supplierCost: z139.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
|
|
33114
|
+
shippingCost: z139.number().finite().nonnegative(),
|
|
33115
|
+
returnRate: z139.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
|
|
33116
|
+
cancellationRate: z139.number().finite().min(0).lt(1)
|
|
33036
33117
|
}).strict();
|
|
33037
|
-
var Count =
|
|
33038
|
-
var ReportProductCountsInput =
|
|
33039
|
-
productControlId:
|
|
33118
|
+
var Count = z139.number().int().min(0).max(1e5).nullable().optional();
|
|
33119
|
+
var ReportProductCountsInput = z139.object({
|
|
33120
|
+
productControlId: z139.string().uuid().describe("El identificador que devuelve get_product_control."),
|
|
33040
33121
|
date: DateOnly2,
|
|
33041
33122
|
orders: Count.describe("Pedidos que entraron ese d\xEDa."),
|
|
33042
33123
|
cancelledOrders: Count,
|
|
@@ -33046,36 +33127,36 @@ var ReportProductCountsInput = z138.object({
|
|
|
33046
33127
|
}).strict();
|
|
33047
33128
|
var MAX_ORDERS_FILE_BYTES = 8 * 1024 * 1024;
|
|
33048
33129
|
var SyncShopifyOrdersInput = SyncShopifyOrdersSchema;
|
|
33049
|
-
var ImportOrdersFileInput =
|
|
33130
|
+
var ImportOrdersFileInput = z139.object({
|
|
33050
33131
|
/** Ruta ABSOLUTA en la máquina de quien usa el MCP. */
|
|
33051
|
-
path:
|
|
33052
|
-
operationId:
|
|
33132
|
+
path: z139.string().min(1),
|
|
33133
|
+
operationId: z139.string().uuid().optional(),
|
|
33053
33134
|
/** Solo mirar: devuelve el resumen y no escribe nada. */
|
|
33054
|
-
dryRun:
|
|
33135
|
+
dryRun: z139.boolean().optional()
|
|
33055
33136
|
}).strict();
|
|
33056
|
-
var CreateOperationInput =
|
|
33057
|
-
countryCode:
|
|
33058
|
-
label:
|
|
33137
|
+
var CreateOperationInput = z139.object({
|
|
33138
|
+
countryCode: z139.string().length(2),
|
|
33139
|
+
label: z139.string().trim().min(1).max(60).optional()
|
|
33059
33140
|
}).strict();
|
|
33060
|
-
var MergeProductsInput =
|
|
33061
|
-
sourceId:
|
|
33062
|
-
targetId:
|
|
33141
|
+
var MergeProductsInput = z139.object({
|
|
33142
|
+
sourceId: z139.string().uuid(),
|
|
33143
|
+
targetId: z139.string().uuid()
|
|
33063
33144
|
}).strict();
|
|
33064
|
-
var SetMonthlyExpenseInput =
|
|
33065
|
-
month:
|
|
33145
|
+
var SetMonthlyExpenseInput = z139.object({
|
|
33146
|
+
month: z139.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
|
|
33066
33147
|
label: SaveMonthlyExpenseSchema.shape.label,
|
|
33067
|
-
amount:
|
|
33068
|
-
operationId:
|
|
33148
|
+
amount: z139.number().finite().min(0),
|
|
33149
|
+
operationId: z139.string().uuid().optional()
|
|
33069
33150
|
}).strict();
|
|
33070
|
-
var SetAdSpendInput =
|
|
33071
|
-
month:
|
|
33072
|
-
operationId:
|
|
33151
|
+
var SetAdSpendInput = z139.object({
|
|
33152
|
+
month: z139.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
|
|
33153
|
+
operationId: z139.string().uuid().optional(),
|
|
33073
33154
|
/** null RETIRA la corrección; cero afirma que no pautó. */
|
|
33074
|
-
amount:
|
|
33155
|
+
amount: z139.number().finite().min(0).nullable()
|
|
33075
33156
|
}).strict();
|
|
33076
|
-
var GetFinancesInput =
|
|
33077
|
-
month:
|
|
33078
|
-
operationId:
|
|
33157
|
+
var GetFinancesInput = z139.object({
|
|
33158
|
+
month: z139.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."),
|
|
33159
|
+
operationId: z139.string().uuid().optional().describe("El negocio, si tiene mas de uno. Con uno solo no hace falta decirlo.")
|
|
33079
33160
|
}).strict();
|
|
33080
33161
|
function registerProductControlTools(server, api, { transport = "http" } = {}) {
|
|
33081
33162
|
server.registerTool("get_product_control", {
|
|
@@ -33267,28 +33348,28 @@ function registerProductControlTools(server, api, { transport = "http" } = {}) {
|
|
|
33267
33348
|
__name(registerProductControlTools, "registerProductControlTools");
|
|
33268
33349
|
|
|
33269
33350
|
// src/tools/market-study-estimate.ts
|
|
33270
|
-
import { z as
|
|
33351
|
+
import { z as z140 } from "zod";
|
|
33271
33352
|
var estimateMarketStudyGuidance = getProductMakerCapabilityModelGuidance("estimate_market_study_cost");
|
|
33272
|
-
var ProductTaskOutputEnum =
|
|
33353
|
+
var ProductTaskOutputEnum = z140.enum([
|
|
33273
33354
|
"landing",
|
|
33274
33355
|
"video",
|
|
33275
33356
|
"image"
|
|
33276
33357
|
]);
|
|
33277
|
-
var EstimateMarketStudyInput =
|
|
33278
|
-
kind:
|
|
33358
|
+
var EstimateMarketStudyInput = z140.object({
|
|
33359
|
+
kind: z140.enum([
|
|
33279
33360
|
"video",
|
|
33280
33361
|
"product_task"
|
|
33281
33362
|
]).describe('"video" para generate_video_creative; "product_task" para create_product_task.'),
|
|
33282
|
-
videoAngleCount:
|
|
33283
|
-
durationSeconds:
|
|
33363
|
+
videoAngleCount: z140.number().int().min(1).max(3).describe("Los mismos 1, 2 o 3 videos que vas a pedir al crear."),
|
|
33364
|
+
durationSeconds: z140.number().int().min(4).max(30).default(15).describe("La misma duraci\xF3n que enviar\xE1s al crear."),
|
|
33284
33365
|
videoStructure: VideoStructureEnum.default("single_take"),
|
|
33285
33366
|
videoQuality: VideoQualityEnum.default("economy"),
|
|
33286
33367
|
videoResolution: VideoResolutionEnum.default("480p"),
|
|
33287
|
-
productImageCount:
|
|
33368
|
+
productImageCount: z140.number().int().min(1).max(5).optional().describe("Cantidad de fotos del producto que adjuntar\xE1s (1-5)."),
|
|
33288
33369
|
outputs: ProductTaskOutputEnum.array().min(1).max(3).optional().describe('Solo para kind="product_task": los mismos outputs de create_product_task. Default: todos.'),
|
|
33289
|
-
generateAiMusic:
|
|
33370
|
+
generateAiMusic: z140.boolean().optional(),
|
|
33290
33371
|
generateAiEdit: AiEditRequestSchema.optional(),
|
|
33291
|
-
replicateWinnerWhenFound:
|
|
33372
|
+
replicateWinnerWhenFound: z140.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.")
|
|
33292
33373
|
}).strict();
|
|
33293
33374
|
function buildMarketStudyEstimateRequests(input) {
|
|
33294
33375
|
const quotesVideo = input.kind === "video" || (input.outputs ?? [
|