@productmaker/mcp 11.7.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 +1438 -973
- package/dist/stdio.js +1438 -973
- 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({
|
|
@@ -10034,20 +10036,287 @@ var TikTokAdvertiserSchema = z53.object({
|
|
|
10034
10036
|
status: z53.string().optional(),
|
|
10035
10037
|
timezone: z53.string().optional()
|
|
10036
10038
|
});
|
|
10039
|
+
var TIKTOK_BUDGET_CURRENCY_POLICY = {
|
|
10040
|
+
USD: [
|
|
10041
|
+
1,
|
|
10042
|
+
0.01
|
|
10043
|
+
],
|
|
10044
|
+
EUR: [
|
|
10045
|
+
1,
|
|
10046
|
+
0.01
|
|
10047
|
+
],
|
|
10048
|
+
JPY: [
|
|
10049
|
+
100,
|
|
10050
|
+
1
|
|
10051
|
+
],
|
|
10052
|
+
HKD: [
|
|
10053
|
+
10,
|
|
10054
|
+
0.01
|
|
10055
|
+
],
|
|
10056
|
+
GBP: [
|
|
10057
|
+
1,
|
|
10058
|
+
0.01
|
|
10059
|
+
],
|
|
10060
|
+
MYR: [
|
|
10061
|
+
1,
|
|
10062
|
+
0.01
|
|
10063
|
+
],
|
|
10064
|
+
RUB: [
|
|
10065
|
+
100,
|
|
10066
|
+
0.01
|
|
10067
|
+
],
|
|
10068
|
+
ZAR: [
|
|
10069
|
+
10,
|
|
10070
|
+
0.01
|
|
10071
|
+
],
|
|
10072
|
+
KRW: [
|
|
10073
|
+
1e3,
|
|
10074
|
+
1
|
|
10075
|
+
],
|
|
10076
|
+
AED: [
|
|
10077
|
+
1,
|
|
10078
|
+
0.01
|
|
10079
|
+
],
|
|
10080
|
+
SAR: [
|
|
10081
|
+
1,
|
|
10082
|
+
0.01
|
|
10083
|
+
],
|
|
10084
|
+
HUF: [
|
|
10085
|
+
1,
|
|
10086
|
+
1
|
|
10087
|
+
],
|
|
10088
|
+
PLN: [
|
|
10089
|
+
4,
|
|
10090
|
+
0.01
|
|
10091
|
+
],
|
|
10092
|
+
DKK: [
|
|
10093
|
+
10,
|
|
10094
|
+
0.01
|
|
10095
|
+
],
|
|
10096
|
+
SEK: [
|
|
10097
|
+
10,
|
|
10098
|
+
0.01
|
|
10099
|
+
],
|
|
10100
|
+
NOK: [
|
|
10101
|
+
10,
|
|
10102
|
+
0.01
|
|
10103
|
+
],
|
|
10104
|
+
TRY: [
|
|
10105
|
+
10,
|
|
10106
|
+
0.01
|
|
10107
|
+
],
|
|
10108
|
+
MXN: [
|
|
10109
|
+
10,
|
|
10110
|
+
0.01
|
|
10111
|
+
],
|
|
10112
|
+
THB: [
|
|
10113
|
+
10,
|
|
10114
|
+
0.01
|
|
10115
|
+
],
|
|
10116
|
+
AUD: [
|
|
10117
|
+
1,
|
|
10118
|
+
0.01
|
|
10119
|
+
],
|
|
10120
|
+
CAD: [
|
|
10121
|
+
1,
|
|
10122
|
+
0.01
|
|
10123
|
+
],
|
|
10124
|
+
NZD: [
|
|
10125
|
+
1,
|
|
10126
|
+
0.01
|
|
10127
|
+
],
|
|
10128
|
+
SGD: [
|
|
10129
|
+
1,
|
|
10130
|
+
0.01
|
|
10131
|
+
],
|
|
10132
|
+
CHF: [
|
|
10133
|
+
1,
|
|
10134
|
+
0.01
|
|
10135
|
+
],
|
|
10136
|
+
CNY: [
|
|
10137
|
+
10,
|
|
10138
|
+
0.01
|
|
10139
|
+
],
|
|
10140
|
+
DZD: [
|
|
10141
|
+
100,
|
|
10142
|
+
0.01
|
|
10143
|
+
],
|
|
10144
|
+
ARS: [
|
|
10145
|
+
1,
|
|
10146
|
+
0.01
|
|
10147
|
+
],
|
|
10148
|
+
BDT: [
|
|
10149
|
+
100,
|
|
10150
|
+
0.01
|
|
10151
|
+
],
|
|
10152
|
+
BOB: [
|
|
10153
|
+
10,
|
|
10154
|
+
0.01
|
|
10155
|
+
],
|
|
10156
|
+
BRL: [
|
|
10157
|
+
1,
|
|
10158
|
+
0.01
|
|
10159
|
+
],
|
|
10160
|
+
CLP: [
|
|
10161
|
+
1e3,
|
|
10162
|
+
1
|
|
10163
|
+
],
|
|
10164
|
+
COP: [
|
|
10165
|
+
1e3,
|
|
10166
|
+
0.01
|
|
10167
|
+
],
|
|
10168
|
+
CRC: [
|
|
10169
|
+
1e3,
|
|
10170
|
+
0.01
|
|
10171
|
+
],
|
|
10172
|
+
CZK: [
|
|
10173
|
+
10,
|
|
10174
|
+
0.01
|
|
10175
|
+
],
|
|
10176
|
+
EGP: [
|
|
10177
|
+
10,
|
|
10178
|
+
0.01
|
|
10179
|
+
],
|
|
10180
|
+
GTQ: [
|
|
10181
|
+
10,
|
|
10182
|
+
0.01
|
|
10183
|
+
],
|
|
10184
|
+
HNL: [
|
|
10185
|
+
10,
|
|
10186
|
+
0.01
|
|
10187
|
+
],
|
|
10188
|
+
ISK: [
|
|
10189
|
+
100,
|
|
10190
|
+
1
|
|
10191
|
+
],
|
|
10192
|
+
INR: [
|
|
10193
|
+
100,
|
|
10194
|
+
0.01
|
|
10195
|
+
],
|
|
10196
|
+
IDR: [
|
|
10197
|
+
1e4,
|
|
10198
|
+
1
|
|
10199
|
+
],
|
|
10200
|
+
ILS: [
|
|
10201
|
+
1,
|
|
10202
|
+
0.01
|
|
10203
|
+
],
|
|
10204
|
+
KES: [
|
|
10205
|
+
100,
|
|
10206
|
+
0.01
|
|
10207
|
+
],
|
|
10208
|
+
MOP: [
|
|
10209
|
+
10,
|
|
10210
|
+
0.01
|
|
10211
|
+
],
|
|
10212
|
+
TWD: [
|
|
10213
|
+
10,
|
|
10214
|
+
1
|
|
10215
|
+
],
|
|
10216
|
+
NIO: [
|
|
10217
|
+
10,
|
|
10218
|
+
0.01
|
|
10219
|
+
],
|
|
10220
|
+
NGN: [
|
|
10221
|
+
100,
|
|
10222
|
+
0.01
|
|
10223
|
+
],
|
|
10224
|
+
PKR: [
|
|
10225
|
+
100,
|
|
10226
|
+
0.01
|
|
10227
|
+
],
|
|
10228
|
+
PYG: [
|
|
10229
|
+
1e4,
|
|
10230
|
+
1
|
|
10231
|
+
],
|
|
10232
|
+
PEN: [
|
|
10233
|
+
1,
|
|
10234
|
+
0.01
|
|
10235
|
+
],
|
|
10236
|
+
PHP: [
|
|
10237
|
+
50,
|
|
10238
|
+
0.01
|
|
10239
|
+
],
|
|
10240
|
+
QAR: [
|
|
10241
|
+
1,
|
|
10242
|
+
0.01
|
|
10243
|
+
],
|
|
10244
|
+
RON: [
|
|
10245
|
+
1,
|
|
10246
|
+
0.01
|
|
10247
|
+
],
|
|
10248
|
+
UAH: [
|
|
10249
|
+
10,
|
|
10250
|
+
0.01
|
|
10251
|
+
],
|
|
10252
|
+
UYU: [
|
|
10253
|
+
10,
|
|
10254
|
+
0.01
|
|
10255
|
+
],
|
|
10256
|
+
VEF: [
|
|
10257
|
+
1e5,
|
|
10258
|
+
0.01
|
|
10259
|
+
],
|
|
10260
|
+
VND: [
|
|
10261
|
+
1e4,
|
|
10262
|
+
1
|
|
10263
|
+
]
|
|
10264
|
+
};
|
|
10265
|
+
var TIKTOK_BUDGET_RULES_BY_CURRENCY = Object.freeze(Object.fromEntries(Object.entries(TIKTOK_BUDGET_CURRENCY_POLICY).map(([currency, policy]) => {
|
|
10266
|
+
if (!policy) throw new Error(`Missing TikTok budget policy for ${currency}`);
|
|
10267
|
+
const [ratio, precision] = policy;
|
|
10268
|
+
return [
|
|
10269
|
+
currency,
|
|
10270
|
+
Object.freeze({
|
|
10271
|
+
minimumDailyBudget: 20 * ratio,
|
|
10272
|
+
maximumDailyBudgetExclusive: 1e7 * ratio,
|
|
10273
|
+
precision
|
|
10274
|
+
})
|
|
10275
|
+
];
|
|
10276
|
+
})));
|
|
10277
|
+
var TIKTOK_DAILY_BUDGET_BY_CURRENCY = Object.freeze(Object.fromEntries(Object.entries(TIKTOK_BUDGET_RULES_BY_CURRENCY).map(([currency, rule]) => [
|
|
10278
|
+
currency,
|
|
10279
|
+
rule?.minimumDailyBudget
|
|
10280
|
+
])));
|
|
10281
|
+
var TikTokBudgetCurrencySchema = z53.string().trim().toUpperCase().regex(/^[A-Z]{3}$/);
|
|
10037
10282
|
var TikTokObjectiveMode = z53.enum([
|
|
10038
10283
|
"auto",
|
|
10039
10284
|
"web_conversions",
|
|
10040
10285
|
"traffic"
|
|
10041
10286
|
]);
|
|
10287
|
+
var TikTokCampaignDestinationSchema = z53.discriminatedUnion("type", [
|
|
10288
|
+
z53.object({
|
|
10289
|
+
type: z53.literal("landing")
|
|
10290
|
+
}),
|
|
10291
|
+
z53.object({
|
|
10292
|
+
type: z53.literal("whatsapp"),
|
|
10293
|
+
phoneNumber: z53.string().trim().regex(/^\+?[1-9][\d\s().-]{6,30}$/).refine((value) => /^\d{7,15}$/.test(value.replace(/\D/g, "")), "Use an international WhatsApp number"),
|
|
10294
|
+
/** Disambiguates shared calling codes such as +1. Independent of ad targeting. */
|
|
10295
|
+
phoneRegionCode: CountryIso2Schema.optional()
|
|
10296
|
+
})
|
|
10297
|
+
]);
|
|
10042
10298
|
var TikTokCampaignConfigSchema = z53.object({
|
|
10043
10299
|
objectiveMode: TikTokObjectiveMode.default("auto"),
|
|
10044
10300
|
/** Optional — service defaults to the currency-aware advertiser minimum
|
|
10045
|
-
|
|
10046
|
-
|
|
10047
|
-
dailyBudget: z53.number().
|
|
10301
|
+
* when omitted (see TikTokService.createCampaignFromCreatives), so
|
|
10302
|
+
* partial overrides ({targetCountry} only) don't 400 at validation. */
|
|
10303
|
+
dailyBudget: z53.number().finite().positive().optional(),
|
|
10304
|
+
/** Required with an explicit dailyBudget; validated against the advertiser. No FX conversion. */
|
|
10305
|
+
budgetCurrency: TikTokBudgetCurrencySchema.optional(),
|
|
10048
10306
|
/** ISO-2 code or resolvable country name — normalized to alpha-2 at validation. */
|
|
10049
10307
|
targetCountry: CountryIso2Schema.optional(),
|
|
10050
|
-
campaignName: z53.string().trim().min(1).max(100).optional()
|
|
10308
|
+
campaignName: z53.string().trim().min(1).max(100).optional(),
|
|
10309
|
+
destination: TikTokCampaignDestinationSchema.optional()
|
|
10310
|
+
}).superRefine((value, ctx) => {
|
|
10311
|
+
if (value.dailyBudget !== void 0 && !value.budgetCurrency) {
|
|
10312
|
+
ctx.addIssue({
|
|
10313
|
+
code: z53.ZodIssueCode.custom,
|
|
10314
|
+
path: [
|
|
10315
|
+
"budgetCurrency"
|
|
10316
|
+
],
|
|
10317
|
+
message: "TIKTOK_BUDGET_CURRENCY_REQUIRED"
|
|
10318
|
+
});
|
|
10319
|
+
}
|
|
10051
10320
|
});
|
|
10052
10321
|
var TikTokCreateCampaignBodySchema = z53.object({
|
|
10053
10322
|
advertiserId: z53.string().min(1, "Debes seleccionar una cuenta publicitaria"),
|
|
@@ -10092,11 +10361,21 @@ var TikTokCreateCampaignFromCreativesBodySchema = z53.object({
|
|
|
10092
10361
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
10093
10362
|
advertiserId: z53.string().min(1),
|
|
10094
10363
|
pixelId: z53.string().optional(),
|
|
10095
|
-
landingUrl: z53.string().url(),
|
|
10364
|
+
landingUrl: z53.string().url().optional(),
|
|
10096
10365
|
/** ISO 3166-1 alpha-2 country code (country names are normalized to alpha-2) */
|
|
10097
10366
|
targetCountry: CountryIso2Schema.default("US"),
|
|
10098
10367
|
campaignName: z53.string().min(1).max(100).optional(),
|
|
10099
10368
|
campaignConfig: TikTokCampaignConfigSchema.optional()
|
|
10369
|
+
}).superRefine((value, ctx) => {
|
|
10370
|
+
if (value.campaignConfig?.destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
10371
|
+
ctx.addIssue({
|
|
10372
|
+
code: z53.ZodIssueCode.custom,
|
|
10373
|
+
path: [
|
|
10374
|
+
"landingUrl"
|
|
10375
|
+
],
|
|
10376
|
+
message: "A landing URL is required for a website destination"
|
|
10377
|
+
});
|
|
10378
|
+
}
|
|
10100
10379
|
});
|
|
10101
10380
|
var TikTokCreateCampaignResponseSchema = z53.object({
|
|
10102
10381
|
campaignId: z53.string(),
|
|
@@ -19232,7 +19511,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
19232
19511
|
], [
|
|
19233
19512
|
"campaign_status"
|
|
19234
19513
|
]),
|
|
19235
|
-
defineCapability("get_video_script", "
|
|
19514
|
+
defineCapability("get_video_script", "e987ff48555dfdab439f9910fb1fa25af373c78c8a9a5ef26458c82add90f2a2", "none", "read_only", [
|
|
19236
19515
|
"read"
|
|
19237
19516
|
], [
|
|
19238
19517
|
slot("task", true, [
|
|
@@ -19628,7 +19907,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
19628
19907
|
], [
|
|
19629
19908
|
"brand"
|
|
19630
19909
|
], MCP_ONLY_CHANNEL_EXPOSURE),
|
|
19631
|
-
defineCapability("create_service_landing", "
|
|
19910
|
+
defineCapability("create_service_landing", "ce99d75f4b00bea23b6bf4f56ef3331b266176cb0a9dead55611ee78d21c41b3", "credit_consuming", "creative", [
|
|
19632
19911
|
"credit_consuming",
|
|
19633
19912
|
"state_change"
|
|
19634
19913
|
], [
|
|
@@ -19706,37 +19985,47 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
19706
19985
|
], [
|
|
19707
19986
|
"commerce_verification"
|
|
19708
19987
|
], MCP_ONLY_CHANNEL_EXPOSURE),
|
|
19709
|
-
defineCapability(
|
|
19988
|
+
defineCapability(
|
|
19989
|
+
"publish_to_tiktok",
|
|
19990
|
+
// MCP 11.7.0: native WhatsApp destination and major-currency budget inputs.
|
|
19991
|
+
"50b932193b62929498ae342417427ef84b1982eb7620c6baffb62c8fb986e66e",
|
|
19710
19992
|
"external_mutation",
|
|
19711
|
-
"
|
|
19712
|
-
|
|
19713
|
-
|
|
19714
|
-
"
|
|
19715
|
-
]
|
|
19716
|
-
|
|
19717
|
-
"
|
|
19718
|
-
|
|
19719
|
-
|
|
19720
|
-
"
|
|
19721
|
-
|
|
19722
|
-
|
|
19723
|
-
"
|
|
19724
|
-
|
|
19725
|
-
|
|
19726
|
-
"
|
|
19727
|
-
|
|
19728
|
-
|
|
19729
|
-
"
|
|
19730
|
-
|
|
19731
|
-
|
|
19732
|
-
"
|
|
19733
|
-
|
|
19734
|
-
|
|
19735
|
-
|
|
19736
|
-
|
|
19737
|
-
|
|
19738
|
-
|
|
19739
|
-
|
|
19993
|
+
"publishing",
|
|
19994
|
+
[
|
|
19995
|
+
"external_mutation",
|
|
19996
|
+
"state_change"
|
|
19997
|
+
],
|
|
19998
|
+
[
|
|
19999
|
+
slot("task", true, [
|
|
20000
|
+
"taskId"
|
|
20001
|
+
]),
|
|
20002
|
+
slot("advertiser", true, [
|
|
20003
|
+
"advertiserId"
|
|
20004
|
+
]),
|
|
20005
|
+
slot("tracking", false, [
|
|
20006
|
+
"pixelId"
|
|
20007
|
+
]),
|
|
20008
|
+
slot("landing", false, [
|
|
20009
|
+
"landingUrl"
|
|
20010
|
+
]),
|
|
20011
|
+
slot("creative_selection", false, [
|
|
20012
|
+
"angleIndexes"
|
|
20013
|
+
]),
|
|
20014
|
+
slot("campaign_configuration", false, [
|
|
20015
|
+
"campaignConfig"
|
|
20016
|
+
]),
|
|
20017
|
+
slot("ad_copy", false, [
|
|
20018
|
+
"primaryText"
|
|
20019
|
+
])
|
|
20020
|
+
],
|
|
20021
|
+
[
|
|
20022
|
+
"creative",
|
|
20023
|
+
"tiktok_advertiser"
|
|
20024
|
+
],
|
|
20025
|
+
[
|
|
20026
|
+
"tiktok_publication"
|
|
20027
|
+
]
|
|
20028
|
+
),
|
|
19740
20029
|
defineCapability("prepare_winning_product_image", "6915790d22256a648374412ac4d11126fe3fabc9404f4552205057f9da3ce0db", "none", "business", [
|
|
19741
20030
|
"state_change"
|
|
19742
20031
|
], [
|
|
@@ -22036,51 +22325,6 @@ var ServicePageGenerateRequestSchema = z104.object({
|
|
|
22036
22325
|
instruction: ServicePageEditInstructionSchema.optional(),
|
|
22037
22326
|
attachmentRefs: ServicePageAttachmentRefsSchema.optional()
|
|
22038
22327
|
}).strict();
|
|
22039
|
-
var SERVICE_PAGE_SOURCES_MISSING_CODES = /* @__PURE__ */ new Set([
|
|
22040
|
-
"SERVICE_PAGE_SOURCES_REQUIRED",
|
|
22041
|
-
"SERVICE_PAGE_SOURCE_TEXT_REQUIRED"
|
|
22042
|
-
]);
|
|
22043
|
-
function isRetryableServicePageFailureCode(errorCode) {
|
|
22044
|
-
return servicePageFailurePolicy(errorCode).retryable;
|
|
22045
|
-
}
|
|
22046
|
-
__name(isRetryableServicePageFailureCode, "isRetryableServicePageFailureCode");
|
|
22047
|
-
function servicePageFailurePolicy(errorCode) {
|
|
22048
|
-
const code = errorCode ?? "";
|
|
22049
|
-
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);
|
|
22050
|
-
if (code.includes("QUOTE_EXPIRED")) return {
|
|
22051
|
-
reason: "quote_expired",
|
|
22052
|
-
retryable
|
|
22053
|
-
};
|
|
22054
|
-
if (code.includes("CREDIT")) return {
|
|
22055
|
-
reason: "credits",
|
|
22056
|
-
retryable
|
|
22057
|
-
};
|
|
22058
|
-
if (code.includes("NOT_FOUND")) return {
|
|
22059
|
-
reason: "not_found",
|
|
22060
|
-
retryable
|
|
22061
|
-
};
|
|
22062
|
-
if (/PROVIDER|ACCEPTANCE/.test(code)) return {
|
|
22063
|
-
reason: "provider",
|
|
22064
|
-
retryable
|
|
22065
|
-
};
|
|
22066
|
-
if (/NETWORK|TIMEOUT|QUEUE/.test(code)) return {
|
|
22067
|
-
reason: "network",
|
|
22068
|
-
retryable
|
|
22069
|
-
};
|
|
22070
|
-
if (/REVISION|STALE|CONFLICT/.test(code)) return {
|
|
22071
|
-
reason: "conflict",
|
|
22072
|
-
retryable
|
|
22073
|
-
};
|
|
22074
|
-
if (/INVALID|PRECONDITION|MISSING|REQUIRED|MISMATCH/.test(code)) return {
|
|
22075
|
-
reason: "validation",
|
|
22076
|
-
retryable
|
|
22077
|
-
};
|
|
22078
|
-
return {
|
|
22079
|
-
reason: "unknown",
|
|
22080
|
-
retryable
|
|
22081
|
-
};
|
|
22082
|
-
}
|
|
22083
|
-
__name(servicePageFailurePolicy, "servicePageFailurePolicy");
|
|
22084
22328
|
var SERVICE_PAGE_CREATION_MAX_ATTEMPTS = 3;
|
|
22085
22329
|
var ServicePageCreationMarkerSchema = z104.object({
|
|
22086
22330
|
rootOperationId: z104.string().uuid().optional(),
|
|
@@ -22232,6 +22476,18 @@ var ServicePagePreviewResponseSchema = z104.object({
|
|
|
22232
22476
|
artifact: ServiceLandingArtifactSchema,
|
|
22233
22477
|
assetsById: z104.record(AssetIdSchema, ServicePagePublicAssetSchema)
|
|
22234
22478
|
}).strict();
|
|
22479
|
+
var ServicePageAdminViewSchema = z104.object({
|
|
22480
|
+
id: z104.string().uuid(),
|
|
22481
|
+
businessName: z104.string().trim().min(1).max(120).nullable(),
|
|
22482
|
+
state: z104.enum([
|
|
22483
|
+
"created",
|
|
22484
|
+
"generating",
|
|
22485
|
+
"error"
|
|
22486
|
+
]),
|
|
22487
|
+
publicUrl: z104.string().url().nullable(),
|
|
22488
|
+
artifact: ServiceLandingArtifactSchema.nullable(),
|
|
22489
|
+
assetsById: z104.record(AssetIdSchema, ServicePagePublicAssetSchema)
|
|
22490
|
+
}).strict();
|
|
22235
22491
|
var ServicePagePreviewRequestSchema = z104.object({
|
|
22236
22492
|
expectedRevision: z104.number().int().nonnegative(),
|
|
22237
22493
|
candidateOperationId: z104.string().uuid().optional(),
|
|
@@ -22759,7 +23015,9 @@ var TaskAdDefaultsSchema = z113.object({
|
|
|
22759
23015
|
]).nullable(),
|
|
22760
23016
|
headline: z113.string().min(1).max(AD_HEADLINE_MAX_CHARS).nullable(),
|
|
22761
23017
|
/** ISO 3166-1 alpha-2 when known. */
|
|
22762
|
-
country: z113.string().min(2).nullable()
|
|
23018
|
+
country: z113.string().min(2).nullable(),
|
|
23019
|
+
/** The task Brand's contact; optional for older API responses. */
|
|
23020
|
+
whatsappNumber: WhatsAppE164Schema.nullable().optional()
|
|
22763
23021
|
});
|
|
22764
23022
|
|
|
22765
23023
|
// ../../packages/types/src/service-website-capture.ts
|
|
@@ -23566,7 +23824,7 @@ var ApiClient = class {
|
|
|
23566
23824
|
};
|
|
23567
23825
|
|
|
23568
23826
|
// src/index.ts
|
|
23569
|
-
var MCP_VERSION = "11.
|
|
23827
|
+
var MCP_VERSION = "11.8.1";
|
|
23570
23828
|
|
|
23571
23829
|
// src/meta-upload-app.ts
|
|
23572
23830
|
import { readFileSync } from "fs";
|
|
@@ -23638,7 +23896,7 @@ function registerMediaUploadAppResource(server, resolveEndpoint) {
|
|
|
23638
23896
|
__name(registerMediaUploadAppResource, "registerMediaUploadAppResource");
|
|
23639
23897
|
|
|
23640
23898
|
// src/tools/tasks.ts
|
|
23641
|
-
import { z as
|
|
23899
|
+
import { z as z121 } from "zod";
|
|
23642
23900
|
|
|
23643
23901
|
// src/resolve-image.ts
|
|
23644
23902
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -24661,6 +24919,13 @@ var McpErrorCode = {
|
|
|
24661
24919
|
META_WHATSAPP_INVALID_NUMBER: "META_WHATSAPP_INVALID_NUMBER",
|
|
24662
24920
|
META_INVALID_CTA: "META_INVALID_CTA",
|
|
24663
24921
|
// TikTok Ads
|
|
24922
|
+
TIKTOK_SERVICE_VIDEO_REQUIRED: "TIKTOK_SERVICE_VIDEO_REQUIRED",
|
|
24923
|
+
TIKTOK_BUDGET_CURRENCY_REQUIRED: "TIKTOK_BUDGET_CURRENCY_REQUIRED",
|
|
24924
|
+
TIKTOK_BUDGET_CURRENCY_UNSUPPORTED: "TIKTOK_BUDGET_CURRENCY_UNSUPPORTED",
|
|
24925
|
+
TIKTOK_BUDGET_CURRENCY_MISMATCH: "TIKTOK_BUDGET_CURRENCY_MISMATCH",
|
|
24926
|
+
TIKTOK_BUDGET_TOO_LOW: "TIKTOK_BUDGET_TOO_LOW",
|
|
24927
|
+
TIKTOK_BUDGET_PRECISION_INVALID: "TIKTOK_BUDGET_PRECISION_INVALID",
|
|
24928
|
+
TIKTOK_BUDGET_TOO_HIGH: "TIKTOK_BUDGET_TOO_HIGH",
|
|
24664
24929
|
TIKTOK_NOT_CONNECTED: "TIKTOK_NOT_CONNECTED",
|
|
24665
24930
|
TIKTOK_TOKEN_EXPIRED: "TIKTOK_TOKEN_EXPIRED",
|
|
24666
24931
|
TIKTOK_PERMISSION_DENIED: "TIKTOK_PERMISSION_DENIED",
|
|
@@ -24675,6 +24940,12 @@ var McpErrorCode = {
|
|
|
24675
24940
|
TIKTOK_RATE_LIMITED: "TIKTOK_RATE_LIMITED",
|
|
24676
24941
|
TIKTOK_SERVER_ERROR: "TIKTOK_SERVER_ERROR",
|
|
24677
24942
|
TIKTOK_INVALID_DATA: "TIKTOK_INVALID_DATA",
|
|
24943
|
+
TIKTOK_WHATSAPP_INVALID_NUMBER: "TIKTOK_WHATSAPP_INVALID_NUMBER",
|
|
24944
|
+
TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED: "TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED",
|
|
24945
|
+
TIKTOK_WHATSAPP_INVALID_OBJECTIVE: "TIKTOK_WHATSAPP_INVALID_OBJECTIVE",
|
|
24946
|
+
TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY: "TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY",
|
|
24947
|
+
TIKTOK_WHATSAPP_VIDEO_REQUIRED: "TIKTOK_WHATSAPP_VIDEO_REQUIRED",
|
|
24948
|
+
TIKTOK_WHATSAPP_UNAVAILABLE: "TIKTOK_WHATSAPP_UNAVAILABLE",
|
|
24678
24949
|
// Actor voice lifecycle (contextual voice cloning — the API throws these as
|
|
24679
24950
|
// Nest ConflictException("VOICE_*"), so they arrive in body.message)
|
|
24680
24951
|
VOICE_FEATURE_DISABLED: "VOICE_FEATURE_DISABLED",
|
|
@@ -24767,6 +25038,19 @@ var CODE_MESSAGES = {
|
|
|
24767
25038
|
TIKTOK_RATE_LIMITED: "TikTok rate-limited the request. Wait a few minutes before retrying.",
|
|
24768
25039
|
TIKTOK_SERVER_ERROR: "TikTok returned a server error. Retry in a few minutes.",
|
|
24769
25040
|
TIKTOK_INVALID_DATA: "TikTok rejected the request due to invalid data. Verify `advertiserId`, `pixelId`, and `campaignConfig`.",
|
|
25041
|
+
TIKTOK_BUDGET_CURRENCY_REQUIRED: "Verify the selected advertiser currency and pass budgetCurrency with an explicit dailyBudget. If the account currency is unavailable, refresh advertiser access; never assume USD or reinterpret a cents amount.",
|
|
25042
|
+
TIKTOK_BUDGET_CURRENCY_UNSUPPORTED: "The selected advertiser currency has no verified TikTok budget policy. Do not guess a default or convert currencies.",
|
|
25043
|
+
TIKTOK_BUDGET_CURRENCY_MISMATCH: "The budget currency differs from the selected advertiser currency. dailyBudgetUsd supports USD accounts only; use dailyBudget plus the exact account budgetCurrency, without guessed FX.",
|
|
25044
|
+
TIKTOK_BUDGET_TOO_LOW: "The daily budget is below the provider minimum in account-currency major units. Ask for a valid explicit amount; never silently increase it.",
|
|
25045
|
+
TIKTOK_BUDGET_PRECISION_INVALID: "The daily budget exceeds the supported precision for the advertiser currency. Ask for an amount with the required precision.",
|
|
25046
|
+
TIKTOK_BUDGET_TOO_HIGH: "The daily budget meets or exceeds the exclusive provider maximum in account-currency major units. Ask for a lower explicit amount.",
|
|
25047
|
+
TIKTOK_SERVICE_VIDEO_REQUIRED: "Service image ads are supported on Meta. For TikTok, choose a service video; never silently omit an image from a mixed selected set.",
|
|
25048
|
+
TIKTOK_WHATSAPP_INVALID_NUMBER: "Use a complete international WhatsApp number including its calling code.",
|
|
25049
|
+
TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED: "Ask the user for the country of this phone number and pass destination.phoneRegionCode. Do not assume it is the target country.",
|
|
25050
|
+
TIKTOK_WHATSAPP_INVALID_OBJECTIVE: "WhatsApp uses native click optimization. Choose a landing destination for web conversions.",
|
|
25051
|
+
TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY: "TikTok does not support native WhatsApp ad targeting in this country. Choose a supported country or a landing page.",
|
|
25052
|
+
TIKTOK_WHATSAPP_VIDEO_REQUIRED: "Native TikTok WhatsApp ads require video creatives. Choose a video or a landing destination for this image.",
|
|
25053
|
+
TIKTOK_WHATSAPP_UNAVAILABLE: "Verify the international WhatsApp number and ask TikTok to enable instant messaging ads for this advertiser. Do not retry as website traffic.",
|
|
24770
25054
|
// Campaign (shared)
|
|
24771
25055
|
CAMPAIGN_NOT_FOUND: "Campaign not found or does not belong to this user. Verify the `campaignId`.",
|
|
24772
25056
|
CAMPAIGN_TASK_NOT_FOUND: "Campaign task not found. Verify the `taskId`.",
|
|
@@ -24891,6 +25175,19 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
24891
25175
|
// Validation-shape backend errors (data-level rejection)
|
|
24892
25176
|
McpErrorCode.META_INVALID_DATA,
|
|
24893
25177
|
McpErrorCode.TIKTOK_INVALID_DATA,
|
|
25178
|
+
McpErrorCode.TIKTOK_SERVICE_VIDEO_REQUIRED,
|
|
25179
|
+
McpErrorCode.TIKTOK_BUDGET_CURRENCY_REQUIRED,
|
|
25180
|
+
McpErrorCode.TIKTOK_BUDGET_CURRENCY_UNSUPPORTED,
|
|
25181
|
+
McpErrorCode.TIKTOK_BUDGET_CURRENCY_MISMATCH,
|
|
25182
|
+
McpErrorCode.TIKTOK_BUDGET_TOO_LOW,
|
|
25183
|
+
McpErrorCode.TIKTOK_BUDGET_PRECISION_INVALID,
|
|
25184
|
+
McpErrorCode.TIKTOK_BUDGET_TOO_HIGH,
|
|
25185
|
+
McpErrorCode.TIKTOK_WHATSAPP_INVALID_NUMBER,
|
|
25186
|
+
McpErrorCode.TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED,
|
|
25187
|
+
McpErrorCode.TIKTOK_WHATSAPP_INVALID_OBJECTIVE,
|
|
25188
|
+
McpErrorCode.TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY,
|
|
25189
|
+
McpErrorCode.TIKTOK_WHATSAPP_VIDEO_REQUIRED,
|
|
25190
|
+
McpErrorCode.TIKTOK_WHATSAPP_UNAVAILABLE,
|
|
24894
25191
|
// Out-of-band user-resolvable auth/billing/connection — caller fixes by
|
|
24895
25192
|
// going to Facebook/TikTok/Shopify and reconnecting, finishing
|
|
24896
25193
|
// verification, or adding a payment method. None of these are transient,
|
|
@@ -25021,6 +25318,19 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
|
|
|
25021
25318
|
McpErrorCode.TIKTOK_RATE_LIMITED,
|
|
25022
25319
|
McpErrorCode.TIKTOK_SERVER_ERROR,
|
|
25023
25320
|
McpErrorCode.TIKTOK_INVALID_DATA,
|
|
25321
|
+
McpErrorCode.TIKTOK_SERVICE_VIDEO_REQUIRED,
|
|
25322
|
+
McpErrorCode.TIKTOK_BUDGET_CURRENCY_REQUIRED,
|
|
25323
|
+
McpErrorCode.TIKTOK_BUDGET_CURRENCY_UNSUPPORTED,
|
|
25324
|
+
McpErrorCode.TIKTOK_BUDGET_CURRENCY_MISMATCH,
|
|
25325
|
+
McpErrorCode.TIKTOK_BUDGET_TOO_LOW,
|
|
25326
|
+
McpErrorCode.TIKTOK_BUDGET_PRECISION_INVALID,
|
|
25327
|
+
McpErrorCode.TIKTOK_BUDGET_TOO_HIGH,
|
|
25328
|
+
McpErrorCode.TIKTOK_WHATSAPP_INVALID_NUMBER,
|
|
25329
|
+
McpErrorCode.TIKTOK_WHATSAPP_PHONE_REGION_REQUIRED,
|
|
25330
|
+
McpErrorCode.TIKTOK_WHATSAPP_INVALID_OBJECTIVE,
|
|
25331
|
+
McpErrorCode.TIKTOK_WHATSAPP_UNSUPPORTED_COUNTRY,
|
|
25332
|
+
McpErrorCode.TIKTOK_WHATSAPP_VIDEO_REQUIRED,
|
|
25333
|
+
McpErrorCode.TIKTOK_WHATSAPP_UNAVAILABLE,
|
|
25024
25334
|
// Campaign (shared)
|
|
25025
25335
|
McpErrorCode.CAMPAIGN_NOT_FOUND,
|
|
25026
25336
|
McpErrorCode.CAMPAIGN_TASK_NOT_FOUND,
|
|
@@ -25200,6 +25510,9 @@ function approveRefusalMessage(reason) {
|
|
|
25200
25510
|
}
|
|
25201
25511
|
__name(approveRefusalMessage, "approveRefusalMessage");
|
|
25202
25512
|
|
|
25513
|
+
// src/tools/task-outputs.ts
|
|
25514
|
+
import { z as z117 } from "zod";
|
|
25515
|
+
|
|
25203
25516
|
// src/asset-proxy.ts
|
|
25204
25517
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
25205
25518
|
var TOKEN_TTL_SECONDS = 3600;
|
|
@@ -25253,8 +25566,64 @@ function buildProxyUrl(opts) {
|
|
|
25253
25566
|
}
|
|
25254
25567
|
__name(buildProxyUrl, "buildProxyUrl");
|
|
25255
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
|
+
|
|
25256
25618
|
// src/tools/task-outputs.ts
|
|
25257
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
|
+
});
|
|
25258
25627
|
function proxifyOutputs(outputs, proxyBaseUrl) {
|
|
25259
25628
|
if (!proxyBaseUrl || !isProxyEnabled()) return outputs;
|
|
25260
25629
|
const rewrite = /* @__PURE__ */ __name((url, filename) => buildProxyUrl({
|
|
@@ -25298,7 +25667,15 @@ function proxifyOutputs(outputs, proxyBaseUrl) {
|
|
|
25298
25667
|
...variant,
|
|
25299
25668
|
url: rewrite(variant.url, `video-${sanitizeFilename(v.angleKey)}-${sanitizeFilename(variant.kind)}.mp4`)
|
|
25300
25669
|
}))
|
|
25301
|
-
}))
|
|
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
|
+
} : {}
|
|
25302
25679
|
};
|
|
25303
25680
|
}
|
|
25304
25681
|
__name(proxifyOutputs, "proxifyOutputs");
|
|
@@ -25308,6 +25685,37 @@ function sanitizeFilename(s) {
|
|
|
25308
25685
|
__name(sanitizeFilename, "sanitizeFilename");
|
|
25309
25686
|
async function fetchTaskOutputs(api, taskId, options) {
|
|
25310
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) {
|
|
25311
25719
|
const videos = (raw.videoGenerators ?? []).map((vg) => {
|
|
25312
25720
|
const variants2 = (vg.variants ?? []).filter((v) => Boolean(v.kind) && Boolean(v.url)).map((v) => ({
|
|
25313
25721
|
kind: v.kind,
|
|
@@ -25392,7 +25800,7 @@ async function fetchTaskOutputs(api, taskId, options) {
|
|
|
25392
25800
|
adCopy: typeof raw.adCreative?.adCopy === "string" && raw.adCreative.adCopy.length ? raw.adCreative.adCopy : void 0
|
|
25393
25801
|
};
|
|
25394
25802
|
}
|
|
25395
|
-
__name(
|
|
25803
|
+
__name(projectTaskOutputs, "projectTaskOutputs");
|
|
25396
25804
|
function renderOutputsMarkdown(outputs) {
|
|
25397
25805
|
const parts = [];
|
|
25398
25806
|
parts.push(`\u2705 **Tu tarea est\xE1 lista** \u2014 [Abrir en Product Maker](${outputs.webappUrl})`);
|
|
@@ -25434,6 +25842,18 @@ function renderOutputsMarkdown(outputs) {
|
|
|
25434
25842
|
}
|
|
25435
25843
|
}
|
|
25436
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
|
+
}
|
|
25437
25857
|
return parts.join("\n");
|
|
25438
25858
|
}
|
|
25439
25859
|
__name(renderOutputsMarkdown, "renderOutputsMarkdown");
|
|
@@ -25477,55 +25897,6 @@ __name(outputGifs, "outputGifs");
|
|
|
25477
25897
|
var PendingPresentationSchema = PendingUserActionSchema;
|
|
25478
25898
|
var VideoScriptSchema = GetVideoScriptForReviewResponse;
|
|
25479
25899
|
|
|
25480
|
-
// src/tools/conversational-review-format.ts
|
|
25481
|
-
function escapeMarkdownText(value) {
|
|
25482
|
-
return value.replace(/\\/g, "\\\\").replace(/([`*_{}[\]()<>#+.!|>])/g, "\\$1").replace(/[\r\n\u2028\u2029]+/g, " ");
|
|
25483
|
-
}
|
|
25484
|
-
__name(escapeMarkdownText, "escapeMarkdownText");
|
|
25485
|
-
function trustedPreviewUrl(raw) {
|
|
25486
|
-
if (/[\u0000-\u001f\u007f]/.test(raw)) return null;
|
|
25487
|
-
try {
|
|
25488
|
-
const url = new URL(raw);
|
|
25489
|
-
if (url.username || url.password) return null;
|
|
25490
|
-
const hostname = url.hostname.toLowerCase();
|
|
25491
|
-
const productMakerHost = hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
|
|
25492
|
-
const localHost = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
25493
|
-
if (url.protocol === "https:" && productMakerHost && (!url.port || url.port === "443")) {
|
|
25494
|
-
return url.toString().replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
25495
|
-
}
|
|
25496
|
-
if (url.protocol === "http:" && localHost) {
|
|
25497
|
-
return url.toString().replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
25498
|
-
}
|
|
25499
|
-
return null;
|
|
25500
|
-
} catch {
|
|
25501
|
-
return null;
|
|
25502
|
-
}
|
|
25503
|
-
}
|
|
25504
|
-
__name(trustedPreviewUrl, "trustedPreviewUrl");
|
|
25505
|
-
function renderPreview(preview2, index) {
|
|
25506
|
-
const label = escapeMarkdownText(preview2.label);
|
|
25507
|
-
const url = trustedPreviewUrl(preview2.previewUrl);
|
|
25508
|
-
return [
|
|
25509
|
-
`### Foto ${index + 1}: ${label}`,
|
|
25510
|
-
...preview2.view ? [
|
|
25511
|
-
`- **Vista:** ${escapeMarkdownText(preview2.view)}`
|
|
25512
|
-
] : [],
|
|
25513
|
-
...url ? [
|
|
25514
|
-
``
|
|
25515
|
-
] : [
|
|
25516
|
-
"- Vista previa externa bloqueada por seguridad. \xC1brela desde ProductMaker Web."
|
|
25517
|
-
]
|
|
25518
|
-
];
|
|
25519
|
-
}
|
|
25520
|
-
__name(renderPreview, "renderPreview");
|
|
25521
|
-
function formatCredits(creditCents) {
|
|
25522
|
-
return new Intl.NumberFormat("es-CO", {
|
|
25523
|
-
minimumFractionDigits: creditCents % 100 === 0 ? 0 : 2,
|
|
25524
|
-
maximumFractionDigits: 2
|
|
25525
|
-
}).format(creditCents / 100);
|
|
25526
|
-
}
|
|
25527
|
-
__name(formatCredits, "formatCredits");
|
|
25528
|
-
|
|
25529
25900
|
// src/tools/conversational-review.ts
|
|
25530
25901
|
var PRODUCT_REFERENCE_LABELS = {
|
|
25531
25902
|
same_product: "Es el mismo producto",
|
|
@@ -25855,29 +26226,29 @@ ${parsed.data.error ? `${escapeMarkdownText(parsed.data.error)}
|
|
|
25855
26226
|
__name(renderVideoScriptMarkdown, "renderVideoScriptMarkdown");
|
|
25856
26227
|
|
|
25857
26228
|
// src/tools/media-presentation.ts
|
|
25858
|
-
import { z as
|
|
25859
|
-
var HttpsPresentationUrlSchema =
|
|
26229
|
+
import { z as z118 } from "zod";
|
|
26230
|
+
var HttpsPresentationUrlSchema = z118.string().url().max(2048).refine((value) => new URL(value).protocol === "https:");
|
|
25860
26231
|
var ProductMakerPreviewUrlSchema = HttpsPresentationUrlSchema.refine((value) => {
|
|
25861
26232
|
const hostname = new URL(value).hostname.toLowerCase();
|
|
25862
26233
|
return hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
|
|
25863
26234
|
}, "preview URL must be owned by ProductMaker");
|
|
25864
|
-
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) =>
|
|
25865
|
-
var PresentationImageSchema =
|
|
25866
|
-
kind:
|
|
26235
|
+
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) => z118.number().int().positive().max(maximum), "positiveIntAtMost");
|
|
26236
|
+
var PresentationImageSchema = z118.object({
|
|
26237
|
+
kind: z118.literal("image"),
|
|
25867
26238
|
url: HttpsPresentationUrlSchema,
|
|
25868
|
-
mimeType:
|
|
26239
|
+
mimeType: z118.enum([
|
|
25869
26240
|
"image/jpeg",
|
|
25870
26241
|
"image/png"
|
|
25871
26242
|
]),
|
|
25872
26243
|
byteSize: positiveIntAtMost(5 * 1024 * 1024),
|
|
25873
26244
|
width: positiveIntAtMost(16384),
|
|
25874
26245
|
height: positiveIntAtMost(16384),
|
|
25875
|
-
alt:
|
|
26246
|
+
alt: z118.string().trim().min(1).max(240)
|
|
25876
26247
|
}).strict();
|
|
25877
26248
|
var McpMediaPresentationSchema = PresentationImageSchema;
|
|
25878
|
-
var GalleryImageSchema =
|
|
26249
|
+
var GalleryImageSchema = z118.object({
|
|
25879
26250
|
url: ProductMakerPreviewUrlSchema,
|
|
25880
|
-
alt:
|
|
26251
|
+
alt: z118.string().trim().min(1).max(240)
|
|
25881
26252
|
});
|
|
25882
26253
|
var MCP_GALLERY_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
25883
26254
|
async function imageBlocks(image, transport) {
|
|
@@ -25934,12 +26305,12 @@ async function buildMcpMediaPresentationContent(rawPresentation, transport = "ht
|
|
|
25934
26305
|
];
|
|
25935
26306
|
}
|
|
25936
26307
|
__name(buildMcpMediaPresentationContent, "buildMcpMediaPresentationContent");
|
|
25937
|
-
var OwnedImagePreviewSchema =
|
|
26308
|
+
var OwnedImagePreviewSchema = z118.object({
|
|
25938
26309
|
url: HttpsPresentationUrlSchema.refine((value) => {
|
|
25939
26310
|
const url = new URL(value);
|
|
25940
26311
|
return !url.username && !url.password && !isPrivateHost(url.hostname);
|
|
25941
26312
|
}, "Preview URL must be public HTTPS without credentials"),
|
|
25942
|
-
alt:
|
|
26313
|
+
alt: z118.string().trim().min(1).max(240)
|
|
25943
26314
|
});
|
|
25944
26315
|
async function buildMcpImagePreviewContent(rawPreview, transport = "http") {
|
|
25945
26316
|
const preview2 = OwnedImagePreviewSchema.safeParse(rawPreview);
|
|
@@ -26022,7 +26393,7 @@ async function buildMcpImageGalleryContent(rawItems, transport = "http") {
|
|
|
26022
26393
|
__name(buildMcpImageGalleryContent, "buildMcpImageGalleryContent");
|
|
26023
26394
|
|
|
26024
26395
|
// src/tools/animated-video.ts
|
|
26025
|
-
import { z as
|
|
26396
|
+
import { z as z119 } from "zod";
|
|
26026
26397
|
|
|
26027
26398
|
// src/tools/paid-confirmation.ts
|
|
26028
26399
|
function describeMarketStudyChoice(enabled) {
|
|
@@ -26271,29 +26642,29 @@ function hookVariantsLabel(count) {
|
|
|
26271
26642
|
return count === 1 ? "1 versi\xF3n del gancho" : `${count} versiones del gancho`;
|
|
26272
26643
|
}
|
|
26273
26644
|
__name(hookVariantsLabel, "hookVariantsLabel");
|
|
26274
|
-
var DurationInput =
|
|
26275
|
-
var CreateAnimatedVideoInput =
|
|
26276
|
-
action:
|
|
26645
|
+
var DurationInput = z119.union(MOTION_GRAPHICS_DURATIONS.map((d) => z119.literal(d)));
|
|
26646
|
+
var CreateAnimatedVideoInput = z119.object({
|
|
26647
|
+
action: z119.enum([
|
|
26277
26648
|
"prepare",
|
|
26278
26649
|
"status",
|
|
26279
26650
|
"cancel",
|
|
26280
26651
|
"generate"
|
|
26281
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."),
|
|
26282
|
-
websiteCaptureId:
|
|
26283
|
-
generationRequestId:
|
|
26284
|
-
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."),
|
|
26285
26656
|
// Plain text, not `.url()`: users type «cal.com», and the web accepts it.
|
|
26286
26657
|
// normalizeWebAddress (shared with the web) adds https:// and refuses
|
|
26287
26658
|
// anything that is not a public http(s) address.
|
|
26288
|
-
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."),
|
|
26289
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.'),
|
|
26290
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.`),
|
|
26291
26662
|
language: MotionGraphicsLanguageSchema.optional().describe('Idioma de los textos del video: "es", "en" o "pt".'),
|
|
26292
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.'),
|
|
26293
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".'),
|
|
26294
|
-
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."),
|
|
26295
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.`),
|
|
26296
|
-
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.")
|
|
26297
26668
|
}).strict();
|
|
26298
26669
|
function captureNextAction(capture) {
|
|
26299
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.";
|
|
@@ -26343,49 +26714,49 @@ var FORMAT_LABELS = {
|
|
|
26343
26714
|
square: "cuadrado 1:1",
|
|
26344
26715
|
horizontal: "horizontal 16:9"
|
|
26345
26716
|
};
|
|
26346
|
-
var AnimatedVideoResultSchema =
|
|
26347
|
-
status:
|
|
26717
|
+
var AnimatedVideoResultSchema = z119.object({
|
|
26718
|
+
status: z119.enum([
|
|
26348
26719
|
"pending",
|
|
26349
26720
|
"done",
|
|
26350
26721
|
"error"
|
|
26351
26722
|
]),
|
|
26352
|
-
serviceMode:
|
|
26353
|
-
finalMp4Url:
|
|
26354
|
-
adCopy:
|
|
26355
|
-
adCopies:
|
|
26356
|
-
error:
|
|
26357
|
-
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({
|
|
26358
26729
|
stage: MotionGraphicsStageSchema,
|
|
26359
26730
|
format: MotionGraphicsFormatSchema,
|
|
26360
|
-
durationSec:
|
|
26361
|
-
width:
|
|
26362
|
-
height:
|
|
26731
|
+
durationSec: z119.number(),
|
|
26732
|
+
width: z119.number().optional(),
|
|
26733
|
+
height: z119.number().optional(),
|
|
26363
26734
|
language: MotionGraphicsLanguageSchema.optional(),
|
|
26364
26735
|
videoQuality: MotionGraphicsVideoQualitySchema.optional().catch(void 0),
|
|
26365
26736
|
videoResolution: MotionGraphicsVideoResolutionSchema.optional().catch(void 0),
|
|
26366
|
-
queuedAt:
|
|
26367
|
-
jobsAhead:
|
|
26368
|
-
stageStartedAt:
|
|
26369
|
-
renderProgress:
|
|
26737
|
+
queuedAt: z119.string().optional(),
|
|
26738
|
+
jobsAhead: z119.number().int().nonnegative().optional(),
|
|
26739
|
+
stageStartedAt: z119.string().optional(),
|
|
26740
|
+
renderProgress: z119.number().optional(),
|
|
26370
26741
|
// AI clip fields (absent on a video paid before clips, or from an API
|
|
26371
26742
|
// that predates them). Lenient: an unreadable value is dropped
|
|
26372
26743
|
// instead of hiding the status.
|
|
26373
|
-
clipCount:
|
|
26374
|
-
clipsUsed:
|
|
26375
|
-
clipPriceCents:
|
|
26376
|
-
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),
|
|
26377
26748
|
// Hook versions (absent from an API or a video before them): the
|
|
26378
26749
|
// count ordered and, once done, the signed URLs of the EXTRA versions
|
|
26379
26750
|
// (hook 2, hook 3); `finalMp4Url` is hook 1.
|
|
26380
|
-
hookVariants:
|
|
26381
|
-
cleanVideoUrl:
|
|
26382
|
-
cleanVariantUrls:
|
|
26383
|
-
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),
|
|
26384
26755
|
// Extra hook versions given back, as the task recorded it once the
|
|
26385
26756
|
// refunds landed (absent until then, and from an API before it).
|
|
26386
|
-
variantsRefunded:
|
|
26387
|
-
activity:
|
|
26388
|
-
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()
|
|
26389
26760
|
}).passthrough()
|
|
26390
26761
|
}).passthrough();
|
|
26391
26762
|
function parseAnimatedVideoResult(value) {
|
|
@@ -26688,10 +27059,10 @@ function registerAnimatedVideoTools(server, api) {
|
|
|
26688
27059
|
__name(registerAnimatedVideoTools, "registerAnimatedVideoTools");
|
|
26689
27060
|
|
|
26690
27061
|
// src/tools/winning-product-image.ts
|
|
26691
|
-
import { z as
|
|
26692
|
-
var WinnerReferenceInput =
|
|
26693
|
-
runId:
|
|
26694
|
-
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)
|
|
26695
27066
|
}).strict().describe("Referencia opaca de un producto devuelto por find_winning_products/get_product_research_run.");
|
|
26696
27067
|
async function resolveWinnerImages(api, winnerReference, images) {
|
|
26697
27068
|
if (!winnerReference) return images;
|
|
@@ -26728,62 +27099,62 @@ var createProductTaskSingleOutputDescription = renderCreateProductTaskAvoidWhenF
|
|
|
26728
27099
|
var CREATE_SERVICE_VIDEO_ON_MCP = PRODUCTMAKER_MCP_CAPABILITY_NAMES.includes("create_service_video");
|
|
26729
27100
|
var listTasksGuidance = getProductMakerCapabilityModelGuidance("list_tasks");
|
|
26730
27101
|
var editTaskDraftGuidance = getProductMakerCapabilityModelGuidance("edit_task_draft");
|
|
26731
|
-
var CreateProductTaskInput =
|
|
27102
|
+
var CreateProductTaskInput = z121.object({
|
|
26732
27103
|
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
26733
27104
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
26734
|
-
projectId:
|
|
26735
|
-
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."),
|
|
26736
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.'),
|
|
26737
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.'),
|
|
26738
|
-
price:
|
|
26739
|
-
compareAtPrice:
|
|
26740
|
-
offer:
|
|
26741
|
-
paymentMethods:
|
|
26742
|
-
productContext:
|
|
26743
|
-
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.'),
|
|
26744
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.'),
|
|
26745
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.'),
|
|
26746
|
-
durationSeconds:
|
|
26747
|
-
|
|
26748
|
-
|
|
27117
|
+
durationSeconds: z121.union([
|
|
27118
|
+
z121.literal(15),
|
|
27119
|
+
z121.literal(30)
|
|
26749
27120
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
26750
|
-
pauseForAngleReview:
|
|
26751
|
-
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."),
|
|
26752
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.'),
|
|
26753
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.'),
|
|
26754
27125
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
26755
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).'),
|
|
26756
|
-
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."),
|
|
26757
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.'),
|
|
26758
|
-
outputs:
|
|
27129
|
+
outputs: z121.array(z121.enum([
|
|
26759
27130
|
"landing",
|
|
26760
27131
|
"video",
|
|
26761
27132
|
"image"
|
|
26762
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}`),
|
|
26763
|
-
includeInitialLandingGif:
|
|
26764
|
-
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."),
|
|
26765
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.'),
|
|
26766
|
-
replicateWinnerWhenFound:
|
|
26767
|
-
brandId:
|
|
26768
|
-
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."),
|
|
26769
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.'),
|
|
26770
|
-
creativeDirection:
|
|
26771
|
-
headline:
|
|
26772
|
-
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."),
|
|
26773
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".'),
|
|
26774
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".')
|
|
26775
27146
|
}).strict();
|
|
26776
|
-
var CreateServiceVideoInput =
|
|
26777
|
-
landingUrl:
|
|
26778
|
-
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.'),
|
|
26779
27150
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
26780
|
-
voiceId:
|
|
26781
|
-
brandId:
|
|
26782
|
-
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([
|
|
26783
27154
|
"automatic",
|
|
26784
27155
|
"guided"
|
|
26785
27156
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
26786
|
-
videoPreset:
|
|
27157
|
+
videoPreset: z121.enum([
|
|
26787
27158
|
"auto",
|
|
26788
27159
|
"ugc_dynamic",
|
|
26789
27160
|
"ugc_how_to",
|
|
@@ -26793,128 +27164,128 @@ var CreateServiceVideoInput = z120.object({
|
|
|
26793
27164
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
26794
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.')
|
|
26795
27166
|
}).strict();
|
|
26796
|
-
var GetTaskStatusInput =
|
|
26797
|
-
taskId:
|
|
26798
|
-
waitSeconds:
|
|
27167
|
+
var GetTaskStatusInput = z121.object({
|
|
27168
|
+
taskId: z121.string().min(1),
|
|
27169
|
+
waitSeconds: z121.number().int().min(0).max(120).optional()
|
|
26799
27170
|
});
|
|
26800
|
-
var ListTasksInput =
|
|
26801
|
-
limit:
|
|
26802
|
-
offset:
|
|
26803
|
-
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()
|
|
26804
27175
|
});
|
|
26805
27176
|
var MCP_REVIEW_LIST_FIELD_MAX = IMAGE_WIZARD_LIST_FIELD_MAX;
|
|
26806
27177
|
var MCP_REVIEW_FEATURE_ITEM_MAX = IMAGE_FEATURE_ITEM_MAX;
|
|
26807
|
-
var EditTaskDraftInput =
|
|
26808
|
-
taskId:
|
|
26809
|
-
angleIndex:
|
|
26810
|
-
angle:
|
|
26811
|
-
angle:
|
|
26812
|
-
buyerPersona:
|
|
26813
|
-
actor:
|
|
26814
|
-
actorId:
|
|
26815
|
-
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([
|
|
26816
27187
|
"female",
|
|
26817
27188
|
"male"
|
|
26818
27189
|
]).optional(),
|
|
26819
|
-
scriptNotes:
|
|
27190
|
+
scriptNotes: z121.string().max(2e3).optional()
|
|
26820
27191
|
})
|
|
26821
27192
|
});
|
|
26822
|
-
var PendingActionPayload =
|
|
26823
|
-
|
|
26824
|
-
kind:
|
|
27193
|
+
var PendingActionPayload = z121.discriminatedUnion("kind", [
|
|
27194
|
+
z121.object({
|
|
27195
|
+
kind: z121.literal("product_reference_set_approval"),
|
|
26825
27196
|
scope: ProductReferenceScopeEnum.optional(),
|
|
26826
|
-
scopeKey:
|
|
26827
|
-
scopeSnapshotId:
|
|
26828
|
-
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(),
|
|
26829
27200
|
decision: ProductReferenceResolutionDecisionEnum,
|
|
26830
27201
|
reason: ProductReferenceRejectionReasonEnum.optional(),
|
|
26831
|
-
affectedRefKeys:
|
|
26832
|
-
affectedPhotoNumbers:
|
|
26833
|
-
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([
|
|
26834
27205
|
"remove",
|
|
26835
27206
|
"replace",
|
|
26836
27207
|
"upload",
|
|
26837
27208
|
"return_to_selection"
|
|
26838
27209
|
]).optional()
|
|
26839
27210
|
}),
|
|
26840
|
-
|
|
26841
|
-
kind:
|
|
26842
|
-
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."),
|
|
26843
27214
|
userChoice: ProductReferenceUserChoiceEnum.optional(),
|
|
26844
|
-
confirmedProductNames:
|
|
27215
|
+
confirmedProductNames: z121.array(z121.string().min(1).max(120)).min(2).max(8).optional(),
|
|
26845
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."),
|
|
26846
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."),
|
|
26847
|
-
useMultiSceneShowcase:
|
|
27218
|
+
useMultiSceneShowcase: z121.boolean().optional().describe("Compatibilidad con confirmaciones antiguas: si es true junto con userChoice=variants, conserva el showcase multiescena.")
|
|
26848
27219
|
}),
|
|
26849
|
-
|
|
26850
|
-
kind:
|
|
27220
|
+
z121.object({
|
|
27221
|
+
kind: z121.literal("angle_review_approval"),
|
|
26851
27222
|
// All edits are optional — empty action body just approves the
|
|
26852
27223
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
26853
27224
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
26854
27225
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
26855
|
-
customAngle:
|
|
27226
|
+
customAngle: z121.string().min(1).max(480).optional(),
|
|
26856
27227
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
26857
27228
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
26858
|
-
painPoints:
|
|
26859
|
-
benefits:
|
|
27229
|
+
painPoints: z121.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
27230
|
+
benefits: z121.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
26860
27231
|
// Same policy as the two fields above: the strictest downstream contract
|
|
26861
27232
|
// wins. The image endpoint caps each feature at 240 while landing/video
|
|
26862
27233
|
// accept 300, and this single action routes to whichever the task is — so
|
|
26863
27234
|
// accepting 300 here turns a 250-character feature on an image task into
|
|
26864
27235
|
// an opaque HTTP 400 after the model already believed it had been saved.
|
|
26865
|
-
features:
|
|
26866
|
-
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(),
|
|
26867
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.'),
|
|
26868
|
-
actor:
|
|
26869
|
-
actorId:
|
|
26870
|
-
creativeDirection:
|
|
26871
|
-
selectedAngleNumbers:
|
|
26872
|
-
angleEdits:
|
|
26873
|
-
angleNumber:
|
|
26874
|
-
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([
|
|
26875
27246
|
"market",
|
|
26876
27247
|
"guided"
|
|
26877
27248
|
]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo."),
|
|
26878
|
-
angle:
|
|
26879
|
-
buyerPersona:
|
|
27249
|
+
angle: z121.string().min(1).max(480).optional(),
|
|
27250
|
+
buyerPersona: z121.string().min(1).max(360).optional(),
|
|
26880
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.'),
|
|
26881
|
-
actor:
|
|
26882
|
-
actorId:
|
|
26883
|
-
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()
|
|
26884
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)."),
|
|
26885
|
-
gapDurationSeconds:
|
|
26886
|
-
|
|
26887
|
-
|
|
27256
|
+
gapDurationSeconds: z121.union([
|
|
27257
|
+
z121.literal(15),
|
|
27258
|
+
z121.literal(30)
|
|
26888
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.")
|
|
26889
27260
|
}),
|
|
26890
|
-
|
|
26891
|
-
kind:
|
|
26892
|
-
angleId:
|
|
26893
|
-
angleNumber:
|
|
26894
|
-
content:
|
|
26895
|
-
creativeDirection:
|
|
26896
|
-
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()
|
|
26897
27268
|
}),
|
|
26898
|
-
|
|
26899
|
-
kind:
|
|
26900
|
-
voiceovers:
|
|
26901
|
-
visualDirections:
|
|
26902
|
-
voiceoversByScene:
|
|
26903
|
-
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()
|
|
26904
27275
|
})
|
|
26905
27276
|
]);
|
|
26906
|
-
var ResolvePendingActionInput =
|
|
26907
|
-
taskId:
|
|
27277
|
+
var ResolvePendingActionInput = z121.object({
|
|
27278
|
+
taskId: z121.string().min(1),
|
|
26908
27279
|
action: PendingActionPayload
|
|
26909
27280
|
}).strict();
|
|
26910
27281
|
function resolveVariantConfirmVideoTier(action) {
|
|
26911
27282
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
26912
27283
|
}
|
|
26913
27284
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
26914
|
-
var StatusForResolveSchema =
|
|
27285
|
+
var StatusForResolveSchema = z121.object({
|
|
26915
27286
|
// Metadata used to select the backend resume contract. Older API responses
|
|
26916
27287
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
26917
|
-
kind:
|
|
27288
|
+
kind: z121.enum([
|
|
26918
27289
|
"autopilot",
|
|
26919
27290
|
"video",
|
|
26920
27291
|
"image",
|
|
@@ -26922,18 +27293,18 @@ var StatusForResolveSchema = z120.object({
|
|
|
26922
27293
|
"mix",
|
|
26923
27294
|
"service"
|
|
26924
27295
|
]).optional(),
|
|
26925
|
-
selectedOutputs:
|
|
27296
|
+
selectedOutputs: z121.array(z121.enum([
|
|
26926
27297
|
"landing",
|
|
26927
27298
|
"video",
|
|
26928
27299
|
"image"
|
|
26929
27300
|
])).optional(),
|
|
26930
|
-
videoAngleCount:
|
|
26931
|
-
ugcFormat:
|
|
27301
|
+
videoAngleCount: z121.number().int().min(1).max(3).optional(),
|
|
27302
|
+
ugcFormat: z121.enum([
|
|
26932
27303
|
"single_8s",
|
|
26933
27304
|
"single_15s",
|
|
26934
27305
|
"double_30s"
|
|
26935
27306
|
]).optional(),
|
|
26936
|
-
videoStyle:
|
|
27307
|
+
videoStyle: z121.enum([
|
|
26937
27308
|
"ugc",
|
|
26938
27309
|
"cartoon_3d",
|
|
26939
27310
|
"product_only",
|
|
@@ -26943,13 +27314,13 @@ var StatusForResolveSchema = z120.object({
|
|
|
26943
27314
|
videoQuality: VideoQualityEnum.optional(),
|
|
26944
27315
|
videoRouteId: VideoRouteIdEnum.optional(),
|
|
26945
27316
|
videoModel: TaskVideoModelEnum.optional(),
|
|
26946
|
-
videoTier:
|
|
27317
|
+
videoTier: z121.enum([
|
|
26947
27318
|
"legacy",
|
|
26948
27319
|
"standard",
|
|
26949
27320
|
"pro",
|
|
26950
27321
|
"single_call"
|
|
26951
27322
|
]).optional(),
|
|
26952
|
-
videoResolution:
|
|
27323
|
+
videoResolution: z121.enum([
|
|
26953
27324
|
"480p",
|
|
26954
27325
|
"720p"
|
|
26955
27326
|
]).optional(),
|
|
@@ -26957,60 +27328,60 @@ var StatusForResolveSchema = z120.object({
|
|
|
26957
27328
|
// A task created before a narrative was blocked/hidden must still parse,
|
|
26958
27329
|
// or resolve_pending_action could never approve it.
|
|
26959
27330
|
narrativeStyle: NarrativeStyle.optional(),
|
|
26960
|
-
pauseForVideoScriptReview:
|
|
26961
|
-
pendingUserAction:
|
|
26962
|
-
kind:
|
|
26963
|
-
candidateGrouping:
|
|
26964
|
-
selectedImageUrls:
|
|
26965
|
-
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(),
|
|
26966
27337
|
// Adaptive reference questions are server-owned. Echo only the evidence
|
|
26967
27338
|
// identity and the choices shown by the current snapshot; never accept a
|
|
26968
27339
|
// client-supplied reference set as authorization.
|
|
26969
|
-
options:
|
|
26970
|
-
mode:
|
|
27340
|
+
options: z121.array(ProductReferenceUserChoiceEnum).max(4).optional(),
|
|
27341
|
+
mode: z121.enum([
|
|
26971
27342
|
"question",
|
|
26972
27343
|
"visual_recovery"
|
|
26973
27344
|
]).optional(),
|
|
26974
27345
|
/** Hand-mirrored from packages/types result.ts — see CLAUDE.md § MCP Server Sync. */
|
|
26975
|
-
canKeepSelection:
|
|
26976
|
-
decisionKey:
|
|
26977
|
-
questionOrdinal:
|
|
26978
|
-
questionCeiling:
|
|
26979
|
-
|
|
26980
|
-
|
|
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)
|
|
26981
27352
|
]).optional(),
|
|
26982
|
-
referenceEvidenceVersion:
|
|
26983
|
-
expectedEvidenceHash:
|
|
26984
|
-
question:
|
|
26985
|
-
prompt:
|
|
26986
|
-
helpText:
|
|
26987
|
-
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({
|
|
26988
27359
|
value: ProductReferenceUserChoiceEnum,
|
|
26989
|
-
label:
|
|
26990
|
-
description:
|
|
27360
|
+
label: z121.string().min(1).max(120),
|
|
27361
|
+
description: z121.string().min(1).max(240).optional()
|
|
26991
27362
|
})).min(2).max(4)
|
|
26992
27363
|
}).optional(),
|
|
26993
27364
|
scope: ProductReferenceScopeEnum.optional(),
|
|
26994
|
-
scopeKey:
|
|
26995
|
-
scopeSnapshotId:
|
|
26996
|
-
scopeHash:
|
|
26997
|
-
baseReferenceSetHash:
|
|
26998
|
-
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([
|
|
26999
27370
|
"preparation",
|
|
27000
27371
|
"final"
|
|
27001
27372
|
]).optional(),
|
|
27002
|
-
selectedRefKeys:
|
|
27003
|
-
allowedDecisions:
|
|
27004
|
-
previews:
|
|
27005
|
-
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)
|
|
27006
27377
|
}).passthrough()).optional(),
|
|
27007
|
-
customAngleDraft:
|
|
27008
|
-
painPointsDraft:
|
|
27009
|
-
benefitsDraft:
|
|
27010
|
-
featuresDraft:
|
|
27011
|
-
angleOptions:
|
|
27012
|
-
index:
|
|
27013
|
-
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)
|
|
27014
27385
|
}).passthrough()).min(1).max(5).optional(),
|
|
27015
27386
|
// Recommendation consumed only when the caller explicitly sends
|
|
27016
27387
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
@@ -27020,17 +27391,17 @@ var StatusForResolveSchema = z120.object({
|
|
|
27020
27391
|
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
27021
27392
|
// dropped here, so the approval handler below could not echo it back on the
|
|
27022
27393
|
// PATCH — the selection looked accepted and reached no render.
|
|
27023
|
-
angles:
|
|
27024
|
-
id:
|
|
27025
|
-
content:
|
|
27026
|
-
creativeDirection:
|
|
27027
|
-
actor:
|
|
27028
|
-
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()
|
|
27029
27400
|
}).passthrough()).optional(),
|
|
27030
|
-
panels:
|
|
27031
|
-
beatId:
|
|
27032
|
-
voiceover:
|
|
27033
|
-
visualDirection:
|
|
27401
|
+
panels: z121.array(z121.object({
|
|
27402
|
+
beatId: z121.string(),
|
|
27403
|
+
voiceover: z121.string(),
|
|
27404
|
+
visualDirection: z121.string()
|
|
27034
27405
|
}).passthrough()).optional()
|
|
27035
27406
|
}).optional()
|
|
27036
27407
|
}).passthrough();
|
|
@@ -27383,8 +27754,8 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
27383
27754
|
server.registerTool("verify_landing_commerce", {
|
|
27384
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.",
|
|
27385
27756
|
inputSchema: {
|
|
27386
|
-
taskId:
|
|
27387
|
-
editId:
|
|
27757
|
+
taskId: z121.string().uuid(),
|
|
27758
|
+
editId: z121.string().uuid()
|
|
27388
27759
|
},
|
|
27389
27760
|
annotations: OVERWRITES_EXTERNAL
|
|
27390
27761
|
}, async (input) => {
|
|
@@ -27575,16 +27946,16 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
27575
27946
|
if (input.search) qs.set("search", input.search);
|
|
27576
27947
|
const path3 = `/v1/tasks${qs.toString() ? "?" + qs.toString() : ""}`;
|
|
27577
27948
|
const r = await api.getJson(path3);
|
|
27578
|
-
const page =
|
|
27579
|
-
items:
|
|
27949
|
+
const page = z121.object({
|
|
27950
|
+
items: z121.array(z121.unknown())
|
|
27580
27951
|
}).passthrough().safeParse(r);
|
|
27581
27952
|
if (!page.success) return asText(r);
|
|
27582
27953
|
return asText({
|
|
27583
27954
|
...page.data,
|
|
27584
27955
|
items: page.data.items.map((item) => {
|
|
27585
|
-
const servicePage =
|
|
27586
|
-
taskId:
|
|
27587
|
-
resourceType:
|
|
27956
|
+
const servicePage = z121.object({
|
|
27957
|
+
taskId: z121.string().uuid(),
|
|
27958
|
+
resourceType: z121.literal("service_page")
|
|
27588
27959
|
}).passthrough().safeParse(item);
|
|
27589
27960
|
if (!servicePage.success) return item;
|
|
27590
27961
|
return {
|
|
@@ -27906,13 +28277,34 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
27906
28277
|
if (isVideoTask) {
|
|
27907
28278
|
body.selectedAngleIndices = selectedAngleIndices;
|
|
27908
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;
|
|
27909
28282
|
return asText({
|
|
27910
28283
|
...r2,
|
|
27911
28284
|
...status.pauseForVideoScriptReview === true ? {
|
|
27912
|
-
reviewNextAction: {
|
|
28285
|
+
reviewNextAction: hasCanonicalKeys ? {
|
|
27913
28286
|
kind: "poll_video_script",
|
|
27914
28287
|
tool: "get_video_script",
|
|
27915
|
-
|
|
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."
|
|
27916
28308
|
}
|
|
27917
28309
|
} : {}
|
|
27918
28310
|
});
|
|
@@ -28053,28 +28445,28 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
28053
28445
|
__name(registerTaskTools, "registerTaskTools");
|
|
28054
28446
|
|
|
28055
28447
|
// src/tools/brands.ts
|
|
28056
|
-
import { z as
|
|
28448
|
+
import { z as z122 } from "zod";
|
|
28057
28449
|
var guidance2 = getProductMakerCapabilityModelGuidance("create_brand");
|
|
28058
28450
|
var BRAND_START_REPLAY_WINDOW_MS = 15 * 60 * 1e3;
|
|
28059
28451
|
var BRAND_NOTES_MAX_CHARS = 2e4;
|
|
28060
28452
|
var BRAND_AMBIGUOUS_REQUEST_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
28061
28453
|
var BRAND_CREATION_REQUEST_LIMIT = 500;
|
|
28062
28454
|
var brandCreationRequests = /* @__PURE__ */ new Map();
|
|
28063
|
-
var CreateBrandInput =
|
|
28064
|
-
action:
|
|
28455
|
+
var CreateBrandInput = z122.object({
|
|
28456
|
+
action: z122.enum([
|
|
28065
28457
|
"start",
|
|
28066
28458
|
"status",
|
|
28067
28459
|
"list"
|
|
28068
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."),
|
|
28069
|
-
sourceUrl:
|
|
28070
|
-
businessNotes:
|
|
28071
|
-
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([
|
|
28072
28464
|
"es",
|
|
28073
28465
|
"en",
|
|
28074
28466
|
"pt"
|
|
28075
28467
|
]).optional().describe("Idioma de la conversaci\xF3n. Solo se usa si la p\xE1gina del negocio no deja claro su idioma."),
|
|
28076
|
-
confirmed:
|
|
28077
|
-
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.")
|
|
28078
28470
|
}).strict();
|
|
28079
28471
|
function normalizeBrandSourceUrl(raw) {
|
|
28080
28472
|
const value = raw.trim();
|
|
@@ -28357,27 +28749,26 @@ function registerBrandTools(server, api, options = {}) {
|
|
|
28357
28749
|
__name(registerBrandTools, "registerBrandTools");
|
|
28358
28750
|
|
|
28359
28751
|
// src/tools/service-landing.ts
|
|
28360
|
-
import { createHash as createHash3 } from "crypto";
|
|
28361
|
-
import { z as
|
|
28752
|
+
import { createHash as createHash3, randomUUID } from "crypto";
|
|
28753
|
+
import { z as z123 } from "zod";
|
|
28362
28754
|
var guidance3 = getProductMakerCapabilityModelGuidance("create_service_landing");
|
|
28363
28755
|
var SERVICE_LANDING_KEY_NAMESPACE = "6f1d8c2a-3b4e-5f60-8a7b-9c0d1e2f3a4b";
|
|
28364
28756
|
var SERVICE_LANDING_INFO_MAX_CHARS = 2e4;
|
|
28365
|
-
var CreateServiceLandingInput =
|
|
28366
|
-
action:
|
|
28757
|
+
var CreateServiceLandingInput = z123.object({
|
|
28758
|
+
action: z123.enum([
|
|
28367
28759
|
"prepare",
|
|
28368
28760
|
"build",
|
|
28369
28761
|
"status",
|
|
28370
28762
|
"publish"
|
|
28371
|
-
]).default("prepare").describe("prepare (gratis) crea o retoma la landing de la marca y da el precio; build (pagado, confirmed=true)
|
|
28372
|
-
brandId:
|
|
28373
|
-
servicePageId:
|
|
28374
|
-
businessInfo:
|
|
28375
|
-
confirmed:
|
|
28376
|
-
maxCredits:
|
|
28377
|
-
operationId:
|
|
28378
|
-
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.")
|
|
28379
28771
|
}).strict();
|
|
28380
|
-
var SERVICE_LANDING_MAX_BUILD_ATTEMPTS = 10;
|
|
28381
28772
|
function serviceKey(...parts) {
|
|
28382
28773
|
return deriveUuidV5(parts.join(":"), SERVICE_LANDING_KEY_NAMESPACE);
|
|
28383
28774
|
}
|
|
@@ -28427,18 +28818,6 @@ function serviceEditorUrl(api, pageId) {
|
|
|
28427
28818
|
}
|
|
28428
28819
|
}
|
|
28429
28820
|
__name(serviceEditorUrl, "serviceEditorUrl");
|
|
28430
|
-
var MISSING_LABELS = {
|
|
28431
|
-
"business.name": "nombre del negocio",
|
|
28432
|
-
services: "servicios (y su precio, si lo tiene)",
|
|
28433
|
-
"contact.channels": "WhatsApp, tel\xE9fono o enlace para reservar",
|
|
28434
|
-
"contact.primaryCta": "c\xF3mo quiere que lo contacten primero"
|
|
28435
|
-
};
|
|
28436
|
-
function describeMissing(fields) {
|
|
28437
|
-
return [
|
|
28438
|
-
...new Set(fields.map((field) => MISSING_LABELS[field]).filter((label) => Boolean(label)))
|
|
28439
|
-
];
|
|
28440
|
-
}
|
|
28441
|
-
__name(describeMissing, "describeMissing");
|
|
28442
28821
|
function summarizeFacts(facts) {
|
|
28443
28822
|
return {
|
|
28444
28823
|
...facts.business.name ? {
|
|
@@ -28461,14 +28840,13 @@ function summarizeFacts(facts) {
|
|
|
28461
28840
|
}
|
|
28462
28841
|
__name(summarizeFacts, "summarizeFacts");
|
|
28463
28842
|
var OPERATION_ERROR_TEXT = {
|
|
28464
|
-
SERVICE_PAGE_SOURCES_REQUIRED: "No hay informaci\xF3n del negocio para escribir la p\xE1gina. Pide al usuario que cuente sus servicios y
|
|
28465
|
-
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."
|
|
28466
28845
|
};
|
|
28467
28846
|
function operationFailureText(code) {
|
|
28468
28847
|
const actionable = code ? OPERATION_ERROR_TEXT[code] : void 0;
|
|
28469
28848
|
if (actionable) return actionable;
|
|
28470
|
-
|
|
28471
|
-
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.";
|
|
28472
28850
|
}
|
|
28473
28851
|
__name(operationFailureText, "operationFailureText");
|
|
28474
28852
|
function isPageGenerated(page) {
|
|
@@ -28483,6 +28861,30 @@ function registerServiceLandingTools(server, api) {
|
|
|
28483
28861
|
return pages.find((page) => page.brandId === brandId) ?? null;
|
|
28484
28862
|
}
|
|
28485
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");
|
|
28486
28888
|
async function addBusinessInfo(page, info) {
|
|
28487
28889
|
try {
|
|
28488
28890
|
await api.postMultipart(`${pagePath(page.id)}/intake`, {}, {
|
|
@@ -28496,24 +28898,39 @@ function registerServiceLandingTools(server, api) {
|
|
|
28496
28898
|
return readPage(page.id);
|
|
28497
28899
|
}
|
|
28498
28900
|
__name(addBusinessInfo, "addBusinessInfo");
|
|
28499
|
-
async function quote(page
|
|
28500
|
-
return api.postJson(`${pagePath(page.id)}/quotes
|
|
28901
|
+
async function quote(page) {
|
|
28902
|
+
return api.postJson(`${pagePath(page.id)}/quotes/generation`, {
|
|
28501
28903
|
expectedRevision: page.draftRevision
|
|
28502
28904
|
});
|
|
28503
28905
|
}
|
|
28504
28906
|
__name(quote, "quote");
|
|
28505
|
-
async function
|
|
28506
|
-
|
|
28507
|
-
|
|
28508
|
-
|
|
28509
|
-
|
|
28510
|
-
|
|
28511
|
-
|
|
28512
|
-
|
|
28513
|
-
|
|
28514
|
-
|
|
28515
|
-
|
|
28516
|
-
|
|
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.");
|
|
28517
28934
|
}
|
|
28518
28935
|
return operation;
|
|
28519
28936
|
}
|
|
@@ -28535,7 +28952,7 @@ function registerServiceLandingTools(server, api) {
|
|
|
28535
28952
|
}
|
|
28536
28953
|
__name(generatedResponse, "generatedResponse");
|
|
28537
28954
|
server.registerTool("create_service_landing", {
|
|
28538
|
-
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.",
|
|
28539
28956
|
inputSchema: CreateServiceLandingInput.shape,
|
|
28540
28957
|
annotations: CREATES_PAID
|
|
28541
28958
|
}, async (rawInput) => {
|
|
@@ -28554,37 +28971,37 @@ function registerServiceLandingTools(server, api) {
|
|
|
28554
28971
|
});
|
|
28555
28972
|
}
|
|
28556
28973
|
const existing = await findPageForBrand(input.brandId);
|
|
28557
|
-
let
|
|
28558
|
-
|
|
28559
|
-
|
|
28560
|
-
|
|
28561
|
-
|
|
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;
|
|
28562
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);
|
|
28563
28988
|
if (input.businessInfo) page2 = await addBusinessInfo(page2, input.businessInfo);
|
|
28564
|
-
const
|
|
28565
|
-
const generationQuote = await quote(page2, "generation");
|
|
28566
|
-
const facts = factsQuote ? quoteRange(factsQuote) : {
|
|
28567
|
-
min: 0,
|
|
28568
|
-
max: 0
|
|
28569
|
-
};
|
|
28989
|
+
const generationQuote = await quote(page2);
|
|
28570
28990
|
const build = quoteRange(generationQuote);
|
|
28571
|
-
const totalMin =
|
|
28572
|
-
const totalCredits =
|
|
28573
|
-
const requiredBalanceCredits =
|
|
28991
|
+
const totalMin = build.min;
|
|
28992
|
+
const totalCredits = build.max;
|
|
28993
|
+
const requiredBalanceCredits = generationQuote.credits;
|
|
28574
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";
|
|
28575
28995
|
const priceText = totalMin === totalCredits ? `${totalCredits} cr\xE9ditos` : `entre ${totalMin} y ${totalCredits} cr\xE9ditos`;
|
|
28576
28996
|
return asText({
|
|
28577
28997
|
servicePageId: page2.id,
|
|
28578
28998
|
charged: false,
|
|
28579
|
-
...
|
|
28999
|
+
...workspace.resumed ? {
|
|
28580
29000
|
resumed: true
|
|
28581
29001
|
} : {},
|
|
28582
29002
|
business: page2.businessName,
|
|
28583
29003
|
known: summarizeFacts(page2.facts),
|
|
28584
29004
|
price: {
|
|
28585
|
-
...factsQuote ? {
|
|
28586
|
-
readBusinessInfoCredits: facts
|
|
28587
|
-
} : {},
|
|
28588
29005
|
buildPageCredits: build,
|
|
28589
29006
|
totalMinCredits: totalMin,
|
|
28590
29007
|
totalCredits,
|
|
@@ -28597,33 +29014,22 @@ function registerServiceLandingTools(server, api) {
|
|
|
28597
29014
|
throw mcp(McpErrorCode.INVALID_INPUT, "servicePageId es obligatorio: usa el que devolvi\xF3 prepare. No se cobr\xF3 nada.");
|
|
28598
29015
|
}
|
|
28599
29016
|
if (input.action === "status") {
|
|
28600
|
-
|
|
28601
|
-
|
|
28602
|
-
|
|
28603
|
-
|
|
28604
|
-
return asText({
|
|
28605
|
-
servicePageId: input.servicePageId,
|
|
28606
|
-
status: creation.status,
|
|
28607
|
-
...editorUrl ? {
|
|
28608
|
-
editorUrl
|
|
28609
|
-
} : {},
|
|
28610
|
-
...creation.status === "failed" ? {
|
|
28611
|
-
errorCode: creation.errorCode,
|
|
28612
|
-
reason: "No pudimos terminar la p\xE1gina. Tus datos siguen guardados."
|
|
28613
|
-
} : {},
|
|
28614
|
-
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."
|
|
28615
|
-
});
|
|
28616
|
-
}
|
|
28617
|
-
}
|
|
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);
|
|
28618
29021
|
let operation2 = null;
|
|
28619
29022
|
if (input.operationId) {
|
|
28620
|
-
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
|
+
}
|
|
28621
29027
|
if (operation2.status === "queued" || operation2.status === "running") {
|
|
28622
29028
|
return asText({
|
|
28623
29029
|
servicePageId: input.servicePageId,
|
|
28624
29030
|
operationId: operation2.opId,
|
|
28625
29031
|
step: operation2.kind === "generation" ? "creando la p\xE1gina" : "leyendo los datos del negocio",
|
|
28626
|
-
status:
|
|
29032
|
+
status: "generating",
|
|
28627
29033
|
nextAction: "Sigue en curso. Llama action=status otra vez con el mismo operationId en unos segundos; no inicies otra etapa."
|
|
28628
29034
|
});
|
|
28629
29035
|
}
|
|
@@ -28631,49 +29037,28 @@ function registerServiceLandingTools(server, api) {
|
|
|
28631
29037
|
return asText({
|
|
28632
29038
|
servicePageId: input.servicePageId,
|
|
28633
29039
|
operationId: operation2.opId,
|
|
28634
|
-
status: "
|
|
29040
|
+
status: "generating",
|
|
28635
29041
|
// Unknown on purpose: the charge is exactly what is being
|
|
28636
29042
|
// confirmed. Settlement only charges a completed step.
|
|
28637
|
-
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."
|
|
28638
29044
|
});
|
|
28639
29045
|
}
|
|
28640
29046
|
if (operation2.status !== "succeeded") {
|
|
28641
29047
|
return asText({
|
|
28642
29048
|
servicePageId: input.servicePageId,
|
|
28643
29049
|
operationId: operation2.opId,
|
|
28644
|
-
status: "
|
|
29050
|
+
status: "error",
|
|
29051
|
+
errorCode: operation2.errorCode,
|
|
28645
29052
|
charged: false,
|
|
28646
29053
|
reason: operationFailureText(operation2.errorCode)
|
|
28647
29054
|
});
|
|
28648
29055
|
}
|
|
28649
29056
|
}
|
|
28650
|
-
|
|
29057
|
+
if (operation2?.status === "succeeded") page2 = await readPage(input.servicePageId);
|
|
28651
29058
|
if (isPageGenerated(page2)) return generatedResponse(page2);
|
|
28652
|
-
|
|
28653
|
-
|
|
28654
|
-
|
|
28655
|
-
servicePageId: page2.id,
|
|
28656
|
-
status: "not_generated",
|
|
28657
|
-
operationStatus: "unknown",
|
|
28658
|
-
...editorUrl ? {
|
|
28659
|
-
editorUrl
|
|
28660
|
-
} : {},
|
|
28661
|
-
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.",
|
|
28662
|
-
nextAction: {
|
|
28663
|
-
tool: "list_tasks",
|
|
28664
|
-
arguments: {}
|
|
28665
|
-
}
|
|
28666
|
-
});
|
|
28667
|
-
}
|
|
28668
|
-
const missing = describeMissing(page2.missingFields);
|
|
28669
|
-
return asText({
|
|
28670
|
-
servicePageId: page2.id,
|
|
28671
|
-
status: page2.factExtractionCompleted ? "business_info_read" : "waiting_for_build",
|
|
28672
|
-
found: summarizeFacts(page2.facts),
|
|
28673
|
-
...missing.length ? {
|
|
28674
|
-
stillMissing: missing
|
|
28675
|
-
} : {},
|
|
28676
|
-
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"
|
|
28677
29062
|
});
|
|
28678
29063
|
}
|
|
28679
29064
|
if (input.action === "publish") {
|
|
@@ -28691,7 +29076,7 @@ function registerServiceLandingTools(server, api) {
|
|
|
28691
29076
|
});
|
|
28692
29077
|
return asText({
|
|
28693
29078
|
servicePageId: page2.id,
|
|
28694
|
-
status: "
|
|
29079
|
+
status: "generated",
|
|
28695
29080
|
publicUrl: published.publicUrl,
|
|
28696
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)"}.`
|
|
28697
29082
|
});
|
|
@@ -28699,7 +29084,8 @@ function registerServiceLandingTools(server, api) {
|
|
|
28699
29084
|
if (serviceApiCode(error)?.includes("SLUG")) {
|
|
28700
29085
|
return asText({
|
|
28701
29086
|
servicePageId: page2.id,
|
|
28702
|
-
status: "
|
|
29087
|
+
status: "generated",
|
|
29088
|
+
publicationErrorCode: "SERVICE_PAGE_SLUG_TAKEN",
|
|
28703
29089
|
slug,
|
|
28704
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.`
|
|
28705
29091
|
});
|
|
@@ -28712,12 +29098,10 @@ function registerServiceLandingTools(server, api) {
|
|
|
28712
29098
|
}
|
|
28713
29099
|
let page = await readPage(input.servicePageId);
|
|
28714
29100
|
if (isPageGenerated(page)) return generatedResponse(page);
|
|
28715
|
-
const
|
|
29101
|
+
const creation = await readCreation(page.id);
|
|
29102
|
+
if (creation.status !== "idle") return creationResponse(page.id, creation);
|
|
28716
29103
|
if (input.businessInfo) page = await addBusinessInfo(page, input.businessInfo);
|
|
28717
|
-
const
|
|
28718
|
-
const needsFacts = !page.factExtractionCompleted || addedInfo;
|
|
28719
|
-
const purpose = needsFacts ? "fact-extraction" : "generation";
|
|
28720
|
-
const quoted = await quote(page, purpose);
|
|
29104
|
+
const quoted = await quote(page);
|
|
28721
29105
|
const credits = quoteRange(quoted).max;
|
|
28722
29106
|
if (credits > input.maxCredits) {
|
|
28723
29107
|
return asText({
|
|
@@ -28727,15 +29111,18 @@ function registerServiceLandingTools(server, api) {
|
|
|
28727
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}.`
|
|
28728
29112
|
});
|
|
28729
29113
|
}
|
|
28730
|
-
await requirePaidCreativeConfirmation(server,
|
|
28731
|
-
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);
|
|
28732
29116
|
return asText({
|
|
28733
29117
|
servicePageId: page.id,
|
|
28734
29118
|
operationId: operation.opId,
|
|
28735
|
-
step:
|
|
28736
|
-
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
|
+
} : {},
|
|
28737
29124
|
priceCredits: credits,
|
|
28738
|
-
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."
|
|
28739
29126
|
});
|
|
28740
29127
|
} catch (error) {
|
|
28741
29128
|
throw translateError(error);
|
|
@@ -28745,7 +29132,7 @@ function registerServiceLandingTools(server, api) {
|
|
|
28745
29132
|
__name(registerServiceLandingTools, "registerServiceLandingTools");
|
|
28746
29133
|
|
|
28747
29134
|
// src/tools/service-images.ts
|
|
28748
|
-
import { z as
|
|
29135
|
+
import { z as z124 } from "zod";
|
|
28749
29136
|
var guidance4 = getProductMakerCapabilityModelGuidance("create_service_images");
|
|
28750
29137
|
var SERVICE_IMAGE_FORMATS = [
|
|
28751
29138
|
"4:5",
|
|
@@ -28758,23 +29145,23 @@ var LANGUAGE_TAGS = {
|
|
|
28758
29145
|
en: "en-US",
|
|
28759
29146
|
pt: "pt-BR"
|
|
28760
29147
|
};
|
|
28761
|
-
var CreateServiceImagesInput =
|
|
28762
|
-
brandId:
|
|
28763
|
-
instruction:
|
|
28764
|
-
delegateToAi:
|
|
28765
|
-
format:
|
|
28766
|
-
count:
|
|
28767
|
-
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([
|
|
28768
29155
|
"standard",
|
|
28769
29156
|
"high"
|
|
28770
29157
|
]).optional().describe('Calidad: "standard" (por defecto) o "high" (m\xE1s cara; no disponible en 4:5).'),
|
|
28771
|
-
language:
|
|
29158
|
+
language: z124.enum([
|
|
28772
29159
|
"es",
|
|
28773
29160
|
"en",
|
|
28774
29161
|
"pt"
|
|
28775
29162
|
]).optional().describe("Idioma de los textos de la imagen (por defecto es)."),
|
|
28776
|
-
adCopy:
|
|
28777
|
-
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.")
|
|
28778
29165
|
}).strict();
|
|
28779
29166
|
function serviceImageModel(quality) {
|
|
28780
29167
|
return quality === "high" ? AD_CREATIVE_MODELS.PREMIUM : AD_CREATIVE_MODELS.STANDARD;
|
|
@@ -28885,7 +29272,7 @@ function registerServiceImageTools(server, api, options = {}) {
|
|
|
28885
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."
|
|
28886
29273
|
});
|
|
28887
29274
|
} catch (error) {
|
|
28888
|
-
if (error instanceof
|
|
29275
|
+
if (error instanceof z124.ZodError) throw invalidInputFromZod(error);
|
|
28889
29276
|
throw translateError(error);
|
|
28890
29277
|
}
|
|
28891
29278
|
});
|
|
@@ -28893,7 +29280,7 @@ function registerServiceImageTools(server, api, options = {}) {
|
|
|
28893
29280
|
__name(registerServiceImageTools, "registerServiceImageTools");
|
|
28894
29281
|
|
|
28895
29282
|
// src/tools/creatives.ts
|
|
28896
|
-
import { z as
|
|
29283
|
+
import { z as z125 } from "zod";
|
|
28897
29284
|
var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
|
|
28898
29285
|
var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
|
|
28899
29286
|
var generateImageCreativesGuidance = getProductMakerCapabilityModelGuidance("generate_image_creatives");
|
|
@@ -28904,72 +29291,72 @@ var CreativeRefs = {
|
|
|
28904
29291
|
images: ImagesInput,
|
|
28905
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.'),
|
|
28906
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.'),
|
|
28907
|
-
price:
|
|
28908
|
-
offer:
|
|
28909
|
-
compareAtPrice:
|
|
28910
|
-
productContext:
|
|
28911
|
-
productLabel:
|
|
28912
|
-
buyerPersona:
|
|
28913
|
-
isCombo:
|
|
28914
|
-
competitorScanEnabled:
|
|
28915
|
-
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.")
|
|
28916
29303
|
};
|
|
28917
|
-
var VideoPresenter =
|
|
28918
|
-
|
|
28919
|
-
mode:
|
|
29304
|
+
var VideoPresenter = z125.discriminatedUnion("mode", [
|
|
29305
|
+
z125.object({
|
|
29306
|
+
mode: z125.literal("none")
|
|
28920
29307
|
}).strict(),
|
|
28921
|
-
|
|
28922
|
-
mode:
|
|
28923
|
-
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.")
|
|
28924
29311
|
}).strict(),
|
|
28925
|
-
|
|
28926
|
-
mode:
|
|
28927
|
-
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.")
|
|
28928
29315
|
}).strict()
|
|
28929
29316
|
]);
|
|
28930
|
-
var VideoCreativeSetup =
|
|
28931
|
-
|
|
28932
|
-
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".')
|
|
28933
29320
|
}).strict(),
|
|
28934
|
-
|
|
28935
|
-
mode:
|
|
29321
|
+
z125.object({
|
|
29322
|
+
mode: z125.literal("guided"),
|
|
28936
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.'),
|
|
28937
|
-
buyerPersona:
|
|
29324
|
+
buyerPersona: z125.string().trim().min(1).max(360),
|
|
28938
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.'),
|
|
28939
29326
|
presenter: VideoPresenter,
|
|
28940
|
-
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.")
|
|
28941
29328
|
}).strict()
|
|
28942
29329
|
]);
|
|
28943
|
-
var VideoInput =
|
|
29330
|
+
var VideoInput = z125.object({
|
|
28944
29331
|
images: CreativeRefs.images.optional(),
|
|
28945
29332
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
28946
|
-
title:
|
|
28947
|
-
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".'),
|
|
28948
29335
|
language: CreativeRefs.language,
|
|
28949
29336
|
country: CreativeRefs.country,
|
|
28950
|
-
aspectRatio:
|
|
29337
|
+
aspectRatio: z125.enum([
|
|
28951
29338
|
"9:16",
|
|
28952
29339
|
"16:9",
|
|
28953
29340
|
"Auto"
|
|
28954
29341
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
28955
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.'),
|
|
28956
|
-
durationSeconds:
|
|
28957
|
-
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."),
|
|
28958
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.'),
|
|
28959
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."),
|
|
28960
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.'),
|
|
28961
29348
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
|
|
28962
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.'),
|
|
28963
|
-
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"].'),
|
|
28964
29351
|
price: CreativeRefs.price,
|
|
28965
29352
|
offer: CreativeRefs.offer,
|
|
28966
29353
|
productContext: CreativeRefs.productContext,
|
|
28967
29354
|
productLabel: CreativeRefs.productLabel,
|
|
28968
|
-
cta:
|
|
28969
|
-
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.'),
|
|
28970
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.'),
|
|
28971
29358
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
28972
|
-
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."),
|
|
28973
29360
|
brandId: CreativeRefs.brandId
|
|
28974
29361
|
}).strict();
|
|
28975
29362
|
var FreeVideoQualityEnum = VideoQualityEnum;
|
|
@@ -28987,7 +29374,7 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
28987
29374
|
length: 29
|
|
28988
29375
|
}, (_, index) => index + 2)
|
|
28989
29376
|
};
|
|
28990
|
-
var FreeVideoModelIdEnum =
|
|
29377
|
+
var FreeVideoModelIdEnum = z125.enum([
|
|
28991
29378
|
"grok-imagine-video-1-5-preview",
|
|
28992
29379
|
"minimax-h3/reference-to-video",
|
|
28993
29380
|
"wan/3-0-video",
|
|
@@ -28998,66 +29385,66 @@ var FREE_SELECTABLE_MODEL_IDS = [
|
|
|
28998
29385
|
"minimax-h3/reference-to-video",
|
|
28999
29386
|
"wan/3-0-video"
|
|
29000
29387
|
];
|
|
29001
|
-
var FreeVideoInput =
|
|
29388
|
+
var FreeVideoInput = z125.object({
|
|
29002
29389
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
29003
|
-
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."),
|
|
29004
29391
|
language: CreativeRefs.language,
|
|
29005
29392
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan).'),
|
|
29006
29393
|
// Retired in MCP 7.0.0 (Task 11): kept in the schema, accepted, and
|
|
29007
29394
|
// parsed ONLY so the handler can reject it with a message naming its
|
|
29008
29395
|
// replacement — dropping it silently would turn a caller's explicit
|
|
29009
29396
|
// Pro++ pick into a quiet fallback to Wan.
|
|
29010
|
-
freeProModel:
|
|
29397
|
+
freeProModel: z125.enum([
|
|
29011
29398
|
"wan",
|
|
29012
29399
|
"seedance"
|
|
29013
29400
|
]).optional().describe("ELIMINADO en MCP 7.0.0 \u2014 usa `modelId`. Enviar este campo produce un error."),
|
|
29014
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.'),
|
|
29015
|
-
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."),
|
|
29016
29403
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
29017
|
-
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.'),
|
|
29018
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.'),
|
|
29019
|
-
addSubtitles:
|
|
29020
|
-
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.")
|
|
29021
29408
|
}).strict();
|
|
29022
29409
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
29023
|
-
var ImageCreativeSetup =
|
|
29024
|
-
|
|
29025
|
-
mode:
|
|
29410
|
+
var ImageCreativeSetup = z125.discriminatedUnion("mode", [
|
|
29411
|
+
z125.object({
|
|
29412
|
+
mode: z125.literal("automatic")
|
|
29026
29413
|
}).strict(),
|
|
29027
|
-
|
|
29028
|
-
mode:
|
|
29029
|
-
creativeArchetype:
|
|
29414
|
+
z125.object({
|
|
29415
|
+
mode: z125.literal("guided"),
|
|
29416
|
+
creativeArchetype: z125.union([
|
|
29030
29417
|
ImageCreativeArchetypeEnum,
|
|
29031
|
-
|
|
29418
|
+
z125.literal("ai_decides")
|
|
29032
29419
|
]),
|
|
29033
|
-
visualTreatment:
|
|
29420
|
+
visualTreatment: z125.union([
|
|
29034
29421
|
ImageCreativeVisualTreatmentEnum,
|
|
29035
|
-
|
|
29422
|
+
z125.literal("ai_decides")
|
|
29036
29423
|
]),
|
|
29037
29424
|
aspectRatio: ImageCreativeAspectRatioEnum,
|
|
29038
|
-
nVariants:
|
|
29039
|
-
|
|
29040
|
-
|
|
29041
|
-
|
|
29042
|
-
|
|
29043
|
-
|
|
29425
|
+
nVariants: z125.union([
|
|
29426
|
+
z125.literal(1),
|
|
29427
|
+
z125.literal(2),
|
|
29428
|
+
z125.literal(3),
|
|
29429
|
+
z125.literal(4),
|
|
29430
|
+
z125.literal(6)
|
|
29044
29431
|
]),
|
|
29045
|
-
imageModel:
|
|
29046
|
-
styleMix:
|
|
29047
|
-
generateAdCopy:
|
|
29048
|
-
competitorScanEnabled:
|
|
29049
|
-
productContext:
|
|
29050
|
-
creativeDirection:
|
|
29051
|
-
brandColors:
|
|
29052
|
-
headline:
|
|
29053
|
-
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()
|
|
29054
29441
|
}).strict()
|
|
29055
29442
|
]).superRefine((setup, context) => {
|
|
29056
29443
|
if (setup.mode !== "guided") return;
|
|
29057
29444
|
const needsStyleMix = setup.nVariants >= 2 && setup.nVariants <= 4;
|
|
29058
29445
|
if (needsStyleMix && setup.styleMix === null) {
|
|
29059
29446
|
context.addIssue({
|
|
29060
|
-
code:
|
|
29447
|
+
code: z125.ZodIssueCode.custom,
|
|
29061
29448
|
path: [
|
|
29062
29449
|
"styleMix"
|
|
29063
29450
|
],
|
|
@@ -29066,7 +29453,7 @@ var ImageCreativeSetup = z124.discriminatedUnion("mode", [
|
|
|
29066
29453
|
}
|
|
29067
29454
|
if (!needsStyleMix && setup.styleMix !== null) {
|
|
29068
29455
|
context.addIssue({
|
|
29069
|
-
code:
|
|
29456
|
+
code: z125.ZodIssueCode.custom,
|
|
29070
29457
|
path: [
|
|
29071
29458
|
"styleMix"
|
|
29072
29459
|
],
|
|
@@ -29075,7 +29462,7 @@ var ImageCreativeSetup = z124.discriminatedUnion("mode", [
|
|
|
29075
29462
|
}
|
|
29076
29463
|
if (imageResolutionOf(setup.imageModel) === "2k" && setup.aspectRatio === "4:5") {
|
|
29077
29464
|
context.addIssue({
|
|
29078
|
-
code:
|
|
29465
|
+
code: z125.ZodIssueCode.custom,
|
|
29079
29466
|
path: [
|
|
29080
29467
|
"aspectRatio"
|
|
29081
29468
|
],
|
|
@@ -29083,55 +29470,55 @@ var ImageCreativeSetup = z124.discriminatedUnion("mode", [
|
|
|
29083
29470
|
});
|
|
29084
29471
|
}
|
|
29085
29472
|
});
|
|
29086
|
-
var LandingCreativeSetup =
|
|
29087
|
-
|
|
29088
|
-
mode:
|
|
29473
|
+
var LandingCreativeSetup = z125.discriminatedUnion("mode", [
|
|
29474
|
+
z125.object({
|
|
29475
|
+
mode: z125.literal("automatic")
|
|
29089
29476
|
}).strict(),
|
|
29090
|
-
|
|
29091
|
-
mode:
|
|
29477
|
+
z125.object({
|
|
29478
|
+
mode: z125.literal("guided"),
|
|
29092
29479
|
landingStrategy: LandingStrategyEnum,
|
|
29093
29480
|
landingImageFormat: LandingImageFormatEnum,
|
|
29094
|
-
competitorScanEnabled:
|
|
29095
|
-
paymentMethods:
|
|
29096
|
-
productContext:
|
|
29097
|
-
creativeDirection:
|
|
29098
|
-
headline:
|
|
29099
|
-
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()
|
|
29100
29487
|
}).strict()
|
|
29101
29488
|
]);
|
|
29102
|
-
var ImageInput =
|
|
29489
|
+
var ImageInput = z125.object({
|
|
29103
29490
|
images: CreativeRefs.images.optional(),
|
|
29104
29491
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
29105
|
-
productTitle:
|
|
29106
|
-
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.'),
|
|
29107
29494
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
29108
29495
|
language: CreativeRefs.language,
|
|
29109
29496
|
country: CreativeRefs.country,
|
|
29110
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."),
|
|
29111
29498
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
29112
|
-
features:
|
|
29499
|
+
features: z125.array(z125.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
29113
29500
|
price: CreativeRefs.price,
|
|
29114
|
-
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."),
|
|
29115
29502
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
29116
29503
|
offer: CreativeRefs.offer,
|
|
29117
29504
|
productLabel: CreativeRefs.productLabel,
|
|
29118
29505
|
isCombo: CreativeRefs.isCombo,
|
|
29119
29506
|
brandId: CreativeRefs.brandId
|
|
29120
29507
|
});
|
|
29121
|
-
var EditImageCreativeInput =
|
|
29122
|
-
taskId:
|
|
29123
|
-
variantIndex:
|
|
29124
|
-
clientRequestId:
|
|
29125
|
-
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".')
|
|
29126
29513
|
});
|
|
29127
|
-
var LandingInput =
|
|
29128
|
-
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."),
|
|
29129
29516
|
images: CreativeRefs.images.optional(),
|
|
29130
29517
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
29131
29518
|
language: CreativeRefs.language,
|
|
29132
29519
|
country: CreativeRefs.country,
|
|
29133
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."),
|
|
29134
|
-
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.'),
|
|
29135
29522
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
29136
29523
|
price: CreativeRefs.price,
|
|
29137
29524
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -29413,8 +29800,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
29413
29800
|
server.registerTool("get_image_edit_status", {
|
|
29414
29801
|
description: imageEditStatusGuidance.summary,
|
|
29415
29802
|
inputSchema: {
|
|
29416
|
-
taskId:
|
|
29417
|
-
operationId:
|
|
29803
|
+
taskId: z125.string().uuid(),
|
|
29804
|
+
operationId: z125.string().uuid()
|
|
29418
29805
|
},
|
|
29419
29806
|
annotations: READS_INTERNAL
|
|
29420
29807
|
}, async (input) => {
|
|
@@ -29471,10 +29858,10 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
29471
29858
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
29472
29859
|
|
|
29473
29860
|
// src/tools/creative-variants.ts
|
|
29474
|
-
import { z as
|
|
29861
|
+
import { z as z126 } from "zod";
|
|
29475
29862
|
var estimateCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("estimate_creative_variant");
|
|
29476
29863
|
var createCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("create_creative_variant");
|
|
29477
|
-
var LandingSectionKeyEnum =
|
|
29864
|
+
var LandingSectionKeyEnum = z126.enum([
|
|
29478
29865
|
"problem_hook",
|
|
29479
29866
|
"problem_cost",
|
|
29480
29867
|
"solution_mechanism",
|
|
@@ -29514,24 +29901,24 @@ var LandingSectionKeyEnum = z125.enum([
|
|
|
29514
29901
|
"lifestyle_fit",
|
|
29515
29902
|
"join_lifestyle"
|
|
29516
29903
|
]);
|
|
29517
|
-
var CreativeVariantKindEnum =
|
|
29904
|
+
var CreativeVariantKindEnum = z126.enum([
|
|
29518
29905
|
"video",
|
|
29519
29906
|
"image",
|
|
29520
29907
|
"landing"
|
|
29521
29908
|
]);
|
|
29522
|
-
var EstimateCreativeVariantInputBase =
|
|
29523
|
-
sourceTaskId:
|
|
29909
|
+
var EstimateCreativeVariantInputBase = z126.object({
|
|
29910
|
+
sourceTaskId: z126.string().uuid().describe("ID de la tarea original completada."),
|
|
29524
29911
|
kind: CreativeVariantKindEnum,
|
|
29525
29912
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
29526
29913
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
29527
|
-
nVariants:
|
|
29914
|
+
nVariants: z126.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
29528
29915
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
29529
|
-
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.")
|
|
29530
29917
|
}).strict();
|
|
29531
29918
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
29532
29919
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
29533
29920
|
ctx.addIssue({
|
|
29534
|
-
code:
|
|
29921
|
+
code: z126.ZodIssueCode.custom,
|
|
29535
29922
|
path: [
|
|
29536
29923
|
"nVariants"
|
|
29537
29924
|
],
|
|
@@ -29541,7 +29928,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29541
29928
|
if (input.kind !== "video") {
|
|
29542
29929
|
if (input.videoPreset !== void 0) {
|
|
29543
29930
|
ctx.addIssue({
|
|
29544
|
-
code:
|
|
29931
|
+
code: z126.ZodIssueCode.custom,
|
|
29545
29932
|
path: [
|
|
29546
29933
|
"videoPreset"
|
|
29547
29934
|
],
|
|
@@ -29550,7 +29937,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29550
29937
|
}
|
|
29551
29938
|
if (input.videoResolution !== void 0) {
|
|
29552
29939
|
ctx.addIssue({
|
|
29553
|
-
code:
|
|
29940
|
+
code: z126.ZodIssueCode.custom,
|
|
29554
29941
|
path: [
|
|
29555
29942
|
"videoResolution"
|
|
29556
29943
|
],
|
|
@@ -29560,7 +29947,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29560
29947
|
}
|
|
29561
29948
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
29562
29949
|
ctx.addIssue({
|
|
29563
|
-
code:
|
|
29950
|
+
code: z126.ZodIssueCode.custom,
|
|
29564
29951
|
path: [
|
|
29565
29952
|
"nVariants"
|
|
29566
29953
|
],
|
|
@@ -29570,7 +29957,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29570
29957
|
if (input.kind !== "landing") {
|
|
29571
29958
|
if (input.sectionKey !== void 0) {
|
|
29572
29959
|
ctx.addIssue({
|
|
29573
|
-
code:
|
|
29960
|
+
code: z126.ZodIssueCode.custom,
|
|
29574
29961
|
path: [
|
|
29575
29962
|
"sectionKey"
|
|
29576
29963
|
],
|
|
@@ -29579,7 +29966,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29579
29966
|
}
|
|
29580
29967
|
if (input.instructions !== void 0) {
|
|
29581
29968
|
ctx.addIssue({
|
|
29582
|
-
code:
|
|
29969
|
+
code: z126.ZodIssueCode.custom,
|
|
29583
29970
|
path: [
|
|
29584
29971
|
"instructions"
|
|
29585
29972
|
],
|
|
@@ -29588,31 +29975,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
29588
29975
|
}
|
|
29589
29976
|
}
|
|
29590
29977
|
});
|
|
29591
|
-
var CreateCreativeVariantInputBase =
|
|
29592
|
-
sourceTaskId:
|
|
29978
|
+
var CreateCreativeVariantInputBase = z126.object({
|
|
29979
|
+
sourceTaskId: z126.string().uuid().describe("ID de la tarea original completada."),
|
|
29593
29980
|
kind: CreativeVariantKindEnum,
|
|
29594
|
-
confirm:
|
|
29595
|
-
clientRequestId:
|
|
29596
|
-
angle:
|
|
29597
|
-
buyerPersona:
|
|
29598
|
-
scriptNotes:
|
|
29599
|
-
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."),
|
|
29600
29987
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
29601
29988
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
29602
|
-
generateAiMusic:
|
|
29603
|
-
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)."),
|
|
29604
29991
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
29605
29992
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
29606
29993
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
29607
29994
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
29608
|
-
styleMix:
|
|
29995
|
+
styleMix: z126.boolean().optional().describe("Opcional para imagen."),
|
|
29609
29996
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
29610
|
-
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.")
|
|
29611
29998
|
}).strict();
|
|
29612
29999
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
29613
30000
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
29614
30001
|
ctx.addIssue({
|
|
29615
|
-
code:
|
|
30002
|
+
code: z126.ZodIssueCode.custom,
|
|
29616
30003
|
path: [
|
|
29617
30004
|
"angle"
|
|
29618
30005
|
],
|
|
@@ -29621,7 +30008,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
29621
30008
|
}
|
|
29622
30009
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
29623
30010
|
ctx.addIssue({
|
|
29624
|
-
code:
|
|
30011
|
+
code: z126.ZodIssueCode.custom,
|
|
29625
30012
|
path: [
|
|
29626
30013
|
"instructions"
|
|
29627
30014
|
],
|
|
@@ -29667,7 +30054,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
29667
30054
|
for (const field of fieldsByKind[input.kind]) {
|
|
29668
30055
|
if (input[field] !== void 0) {
|
|
29669
30056
|
ctx.addIssue({
|
|
29670
|
-
code:
|
|
30057
|
+
code: z126.ZodIssueCode.custom,
|
|
29671
30058
|
path: [
|
|
29672
30059
|
field
|
|
29673
30060
|
],
|
|
@@ -29723,8 +30110,10 @@ function registerCreativeVariantTools(server, api) {
|
|
|
29723
30110
|
inputSchema: CreateCreativeVariantInputBase.shape,
|
|
29724
30111
|
annotations: CREATES_PAID
|
|
29725
30112
|
}, async (input) => {
|
|
30113
|
+
let sourceTaskId;
|
|
29726
30114
|
try {
|
|
29727
30115
|
const validatedInput = parseVariantInput(CreateCreativeVariantInput, input);
|
|
30116
|
+
sourceTaskId = validatedInput.sourceTaskId;
|
|
29728
30117
|
const estimateInput = EstimateCreativeVariantInput.parse({
|
|
29729
30118
|
sourceTaskId: validatedInput.sourceTaskId,
|
|
29730
30119
|
kind: validatedInput.kind,
|
|
@@ -29735,7 +30124,7 @@ function registerCreativeVariantTools(server, api) {
|
|
|
29735
30124
|
instructions: validatedInput.instructions
|
|
29736
30125
|
});
|
|
29737
30126
|
const estimate = await api.postJson(`/v1/tasks/${encodeURIComponent(validatedInput.sourceTaskId)}/creative-variants/estimate`, estimatePayload(estimateInput));
|
|
29738
|
-
if (!estimate.hasSufficientBalance) {
|
|
30127
|
+
if (validatedInput.kind === "landing" && !validatedInput.sectionKey && !estimate.hasSufficientBalance) {
|
|
29739
30128
|
throw mcp(McpErrorCode.INSUFFICIENT_CREDITS, `Saldo insuficiente. La variante requiere hasta ${estimate.estimatedMaxCostCredits} cr\xE9ditos y tu saldo es ${estimate.userBalanceCredits}.`);
|
|
29740
30129
|
}
|
|
29741
30130
|
let sectionKey = validatedInput.sectionKey;
|
|
@@ -29793,20 +30182,43 @@ function registerCreativeVariantTools(server, api) {
|
|
|
29793
30182
|
estimate
|
|
29794
30183
|
});
|
|
29795
30184
|
} catch (error) {
|
|
29796
|
-
|
|
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
|
+
};
|
|
29797
30209
|
}
|
|
29798
30210
|
});
|
|
29799
30211
|
}
|
|
29800
30212
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
29801
30213
|
|
|
29802
30214
|
// src/tools/script-review.ts
|
|
29803
|
-
import { z as
|
|
30215
|
+
import { z as z127 } from "zod";
|
|
29804
30216
|
var getVideoScriptGuidance = getProductMakerCapabilityModelGuidance("get_video_script");
|
|
29805
30217
|
var regenerateVideoScriptGuidance = getProductMakerCapabilityModelGuidance("regenerate_video_script");
|
|
29806
30218
|
var approveVideoScriptGuidance = getProductMakerCapabilityModelGuidance("approve_video_script");
|
|
29807
|
-
var GetVideoScriptInput =
|
|
29808
|
-
taskId:
|
|
29809
|
-
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.')
|
|
29810
30222
|
}).strict();
|
|
29811
30223
|
var ApproveVideoScriptInput = ApproveVideoScriptCapabilityRequest;
|
|
29812
30224
|
var RegenerateVideoScriptInput = RegenerateVideoScriptCapabilityRequest.extend({
|
|
@@ -29814,7 +30226,7 @@ var RegenerateVideoScriptInput = RegenerateVideoScriptCapabilityRequest.extend({
|
|
|
29814
30226
|
});
|
|
29815
30227
|
function registerScriptReviewTools(server, api) {
|
|
29816
30228
|
server.registerTool("get_video_script", {
|
|
29817
|
-
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.',
|
|
29818
30230
|
inputSchema: GetVideoScriptInput.shape,
|
|
29819
30231
|
annotations: READS_INTERNAL
|
|
29820
30232
|
}, async (input) => {
|
|
@@ -29822,6 +30234,28 @@ function registerScriptReviewTools(server, api) {
|
|
|
29822
30234
|
const qs = input.angleKey ? `?angleKey=${encodeURIComponent(input.angleKey)}` : "";
|
|
29823
30235
|
const r = await api.getJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/video-script${qs}`);
|
|
29824
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
|
+
}
|
|
29825
30259
|
return review ? {
|
|
29826
30260
|
content: [
|
|
29827
30261
|
{
|
|
@@ -29850,7 +30284,7 @@ function registerScriptReviewTools(server, api) {
|
|
|
29850
30284
|
}
|
|
29851
30285
|
});
|
|
29852
30286
|
server.registerTool("regenerate_video_script", {
|
|
29853
|
-
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.',
|
|
29854
30288
|
inputSchema: RegenerateVideoScriptInput.shape,
|
|
29855
30289
|
annotations: OVERWRITES_EXTERNAL
|
|
29856
30290
|
}, async (input) => {
|
|
@@ -29889,7 +30323,7 @@ function registerScriptReviewTools(server, api) {
|
|
|
29889
30323
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
29890
30324
|
|
|
29891
30325
|
// src/tools/connections.ts
|
|
29892
|
-
import { z as
|
|
30326
|
+
import { z as z128 } from "zod";
|
|
29893
30327
|
var listShopifyShopsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_shops");
|
|
29894
30328
|
var listShopifyProductsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_products");
|
|
29895
30329
|
var listMetaAdAccountsGuidance = getProductMakerCapabilityModelGuidance("list_meta_ad_accounts");
|
|
@@ -29898,7 +30332,7 @@ var listTikTokAdvertisersGuidance = getProductMakerCapabilityModelGuidance("list
|
|
|
29898
30332
|
function registerConnectionTools(server, api) {
|
|
29899
30333
|
server.registerTool("list_shopify_shops", {
|
|
29900
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.",
|
|
29901
|
-
inputSchema:
|
|
30335
|
+
inputSchema: z128.object({}).shape,
|
|
29902
30336
|
annotations: READS_INTERNAL
|
|
29903
30337
|
}, async () => {
|
|
29904
30338
|
try {
|
|
@@ -29910,10 +30344,10 @@ function registerConnectionTools(server, api) {
|
|
|
29910
30344
|
});
|
|
29911
30345
|
server.registerTool("list_shopify_products", {
|
|
29912
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.',
|
|
29913
|
-
inputSchema:
|
|
29914
|
-
shopDomain:
|
|
29915
|
-
query:
|
|
29916
|
-
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()
|
|
29917
30351
|
}).shape,
|
|
29918
30352
|
annotations: READS_EXTERNAL
|
|
29919
30353
|
}, async ({ shopDomain, query, taskId }) => {
|
|
@@ -29931,7 +30365,7 @@ function registerConnectionTools(server, api) {
|
|
|
29931
30365
|
});
|
|
29932
30366
|
server.registerTool("list_meta_ad_accounts", {
|
|
29933
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.",
|
|
29934
|
-
inputSchema:
|
|
30368
|
+
inputSchema: z128.object({}).shape,
|
|
29935
30369
|
annotations: READS_EXTERNAL
|
|
29936
30370
|
}, async () => {
|
|
29937
30371
|
try {
|
|
@@ -29943,7 +30377,7 @@ function registerConnectionTools(server, api) {
|
|
|
29943
30377
|
});
|
|
29944
30378
|
server.registerTool("list_meta_destinations", {
|
|
29945
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.",
|
|
29946
|
-
inputSchema:
|
|
30380
|
+
inputSchema: z128.object({}).shape,
|
|
29947
30381
|
annotations: READS_EXTERNAL,
|
|
29948
30382
|
_meta: {
|
|
29949
30383
|
"openai/widgetAccessible": true
|
|
@@ -29958,7 +30392,7 @@ function registerConnectionTools(server, api) {
|
|
|
29958
30392
|
});
|
|
29959
30393
|
server.registerTool("list_tiktok_advertisers", {
|
|
29960
30394
|
description: listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.",
|
|
29961
|
-
inputSchema:
|
|
30395
|
+
inputSchema: z128.object({}).shape,
|
|
29962
30396
|
annotations: READS_EXTERNAL
|
|
29963
30397
|
}, async () => {
|
|
29964
30398
|
try {
|
|
@@ -29972,7 +30406,7 @@ function registerConnectionTools(server, api) {
|
|
|
29972
30406
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
29973
30407
|
|
|
29974
30408
|
// src/tools/actors.ts
|
|
29975
|
-
import { z as
|
|
30409
|
+
import { z as z129 } from "zod";
|
|
29976
30410
|
var listActorsGuidance = getProductMakerCapabilityModelGuidance("list_actors");
|
|
29977
30411
|
var actorVoiceGuidance = {
|
|
29978
30412
|
capabilities: getProductMakerCapabilityModelGuidance("get_actor_voice_capabilities"),
|
|
@@ -30012,7 +30446,7 @@ function sanitizeVoiceResult(value) {
|
|
|
30012
30446
|
__name(sanitizeVoiceResult, "sanitizeVoiceResult");
|
|
30013
30447
|
function registerActorTools(server, api) {
|
|
30014
30448
|
const actor = {
|
|
30015
|
-
actorId:
|
|
30449
|
+
actorId: z129.string().min(1).max(64)
|
|
30016
30450
|
};
|
|
30017
30451
|
const safe = /* @__PURE__ */ __name(async (request) => {
|
|
30018
30452
|
try {
|
|
@@ -30041,8 +30475,8 @@ function registerActorTools(server, api) {
|
|
|
30041
30475
|
inputSchema: {
|
|
30042
30476
|
...actor,
|
|
30043
30477
|
draft: ActorVoiceDraftSchema,
|
|
30044
|
-
requestKey:
|
|
30045
|
-
acceptedQuoteId:
|
|
30478
|
+
requestKey: z129.string().trim().min(1).max(128),
|
|
30479
|
+
acceptedQuoteId: z129.string().trim().min(1).max(128)
|
|
30046
30480
|
},
|
|
30047
30481
|
annotations: CREATES_PAID
|
|
30048
30482
|
}, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/previews`, body)));
|
|
@@ -30050,7 +30484,7 @@ function registerActorTools(server, api) {
|
|
|
30050
30484
|
description: actorVoiceGuidance.get.summary,
|
|
30051
30485
|
inputSchema: {
|
|
30052
30486
|
...actor,
|
|
30053
|
-
candidateId:
|
|
30487
|
+
candidateId: z129.string().min(1).max(64)
|
|
30054
30488
|
},
|
|
30055
30489
|
// Polling can resume the first paid submission of an accepted quote.
|
|
30056
30490
|
// The immutable candidate/operation key keeps recovery idempotent.
|
|
@@ -30063,16 +30497,16 @@ function registerActorTools(server, api) {
|
|
|
30063
30497
|
description: actorVoiceGuidance.approve.summary,
|
|
30064
30498
|
inputSchema: {
|
|
30065
30499
|
...actor,
|
|
30066
|
-
candidateId:
|
|
30067
|
-
expectedRevision:
|
|
30068
|
-
expectedApprovedRevision:
|
|
30500
|
+
candidateId: z129.string().min(1).max(64),
|
|
30501
|
+
expectedRevision: z129.number().int().positive(),
|
|
30502
|
+
expectedApprovedRevision: z129.number().int().positive().nullable().optional()
|
|
30069
30503
|
},
|
|
30070
30504
|
// Replaces the selected voice and withdraws the old catalog grants.
|
|
30071
30505
|
annotations: OVERWRITES_INTERNAL
|
|
30072
30506
|
}, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/approve`, body)));
|
|
30073
30507
|
server.registerTool("list_actors", {
|
|
30074
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.",
|
|
30075
|
-
inputSchema:
|
|
30509
|
+
inputSchema: z129.object({}).shape,
|
|
30076
30510
|
annotations: READS_INTERNAL
|
|
30077
30511
|
}, async () => {
|
|
30078
30512
|
try {
|
|
@@ -30105,7 +30539,7 @@ function registerActorTools(server, api) {
|
|
|
30105
30539
|
__name(registerActorTools, "registerActorTools");
|
|
30106
30540
|
|
|
30107
30541
|
// src/tools/publish.ts
|
|
30108
|
-
import { z as
|
|
30542
|
+
import { z as z130 } from "zod";
|
|
30109
30543
|
|
|
30110
30544
|
// src/tools/meta-publish-attempt-id.ts
|
|
30111
30545
|
var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
|
|
@@ -30144,40 +30578,40 @@ var publishToShopifyGuidance = getProductMakerCapabilityModelGuidance("publish_t
|
|
|
30144
30578
|
var publishToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_to_meta");
|
|
30145
30579
|
var publishCreativesToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_creatives_to_meta");
|
|
30146
30580
|
var publishToTikTokGuidance = getProductMakerCapabilityModelGuidance("publish_to_tiktok");
|
|
30147
|
-
var ShopifyInput =
|
|
30148
|
-
taskId:
|
|
30149
|
-
shopDomain:
|
|
30581
|
+
var ShopifyInput = z130.object({
|
|
30582
|
+
taskId: z130.string().min(1),
|
|
30583
|
+
shopDomain: z130.string().min(1),
|
|
30150
30584
|
/** Override: number of landing images to include. Default = ALL images the
|
|
30151
30585
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
30152
30586
|
* generously here; the backend validates each index against
|
|
30153
30587
|
* landing.images.length and rejects out-of-range. */
|
|
30154
|
-
numLandingImages:
|
|
30155
|
-
variantIndex:
|
|
30588
|
+
numLandingImages: z130.number().int().min(1).max(20).optional(),
|
|
30589
|
+
variantIndex: z130.number().int().min(0).optional(),
|
|
30156
30590
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
30157
30591
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
30158
30592
|
* a single-option product page. Defaults to false. */
|
|
30159
|
-
showVariantPicker:
|
|
30593
|
+
showVariantPicker: z130.boolean().optional(),
|
|
30160
30594
|
/** Default `auto` updates the product previously published for this task and
|
|
30161
30595
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
30162
30596
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
30163
|
-
publicationMode:
|
|
30597
|
+
publicationMode: z130.enum([
|
|
30164
30598
|
"auto",
|
|
30165
30599
|
"existing",
|
|
30166
30600
|
"new"
|
|
30167
30601
|
]).optional(),
|
|
30168
30602
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
30169
30603
|
* publicationMode is `existing`. */
|
|
30170
|
-
targetProductId:
|
|
30604
|
+
targetProductId: z130.string().trim().min(1).max(300).optional(),
|
|
30171
30605
|
/** Add an announcement bar text above add-to-cart (optional). */
|
|
30172
|
-
announcementBarEnabled:
|
|
30173
|
-
announcementBarText:
|
|
30606
|
+
announcementBarEnabled: z130.boolean().optional(),
|
|
30607
|
+
announcementBarText: z130.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
|
|
30174
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."),
|
|
30175
|
-
confirmed:
|
|
30609
|
+
confirmed: z130.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
|
|
30176
30610
|
});
|
|
30177
30611
|
var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value, ctx) => {
|
|
30178
30612
|
if (value.commerceCorrection && value.confirmed !== true) {
|
|
30179
30613
|
ctx.addIssue({
|
|
30180
|
-
code:
|
|
30614
|
+
code: z130.ZodIssueCode.custom,
|
|
30181
30615
|
path: [
|
|
30182
30616
|
"confirmed"
|
|
30183
30617
|
],
|
|
@@ -30186,7 +30620,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
30186
30620
|
}
|
|
30187
30621
|
if (value.announcementBarEnabled && !value.announcementBarText) {
|
|
30188
30622
|
ctx.addIssue({
|
|
30189
|
-
code:
|
|
30623
|
+
code: z130.ZodIssueCode.custom,
|
|
30190
30624
|
path: [
|
|
30191
30625
|
"announcementBarText"
|
|
30192
30626
|
],
|
|
@@ -30195,7 +30629,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
30195
30629
|
}
|
|
30196
30630
|
if (!value.announcementBarEnabled && value.announcementBarText) {
|
|
30197
30631
|
ctx.addIssue({
|
|
30198
|
-
code:
|
|
30632
|
+
code: z130.ZodIssueCode.custom,
|
|
30199
30633
|
path: [
|
|
30200
30634
|
"announcementBarText"
|
|
30201
30635
|
],
|
|
@@ -30209,91 +30643,90 @@ function parseShopifyInput(rawInput) {
|
|
|
30209
30643
|
return parsed.data;
|
|
30210
30644
|
}
|
|
30211
30645
|
__name(parseShopifyInput, "parseShopifyInput");
|
|
30212
|
-
var McpCampaignConfig =
|
|
30213
|
-
/**
|
|
30214
|
-
*
|
|
30215
|
-
|
|
30216
|
-
dailyBudgetUsd: z129.number().positive().optional(),
|
|
30646
|
+
var McpCampaignConfig = z130.object({
|
|
30647
|
+
/** Legacy USD amount. Meta retains its existing conversion; the TikTok
|
|
30648
|
+
* schema below restricts this input to USD advertiser accounts. */
|
|
30649
|
+
dailyBudgetUsd: z130.number().positive().optional(),
|
|
30217
30650
|
/** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
|
|
30218
30651
|
* normalizes at validation). Meta accepts max 1 (single-country ad set).
|
|
30219
30652
|
* TikTok takes only the first entry. */
|
|
30220
|
-
targetCountries:
|
|
30221
|
-
campaignName:
|
|
30653
|
+
targetCountries: z130.array(CountryIso2Schema).max(1).optional(),
|
|
30654
|
+
campaignName: z130.string().trim().min(1).max(100).optional()
|
|
30222
30655
|
});
|
|
30223
30656
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
30224
|
-
var MetaInput =
|
|
30225
|
-
taskId:
|
|
30657
|
+
var MetaInput = z130.object({
|
|
30658
|
+
taskId: z130.string().min(1),
|
|
30226
30659
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
30227
|
-
portfolioId:
|
|
30660
|
+
portfolioId: z130.string().trim().min(1),
|
|
30228
30661
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
30229
|
-
pageId:
|
|
30662
|
+
pageId: z130.string().trim().min(1),
|
|
30230
30663
|
adAccountId: McpAdAccountId,
|
|
30231
|
-
pixelId:
|
|
30232
|
-
landingUrl:
|
|
30664
|
+
pixelId: z130.string().min(1).optional(),
|
|
30665
|
+
landingUrl: z130.string().url().optional(),
|
|
30233
30666
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
30234
30667
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
30235
|
-
angleIndexes:
|
|
30668
|
+
angleIndexes: z130.array(z130.number().int().nonnegative()).optional(),
|
|
30236
30669
|
campaignConfig: McpCampaignConfig.optional(),
|
|
30237
30670
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
30238
30671
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
30239
30672
|
* (Meta's primary-text limit). */
|
|
30240
|
-
primaryText:
|
|
30673
|
+
primaryText: z130.string().trim().min(1).max(500).optional(),
|
|
30241
30674
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
30242
30675
|
* (wa.me) instead of a landing page. */
|
|
30243
|
-
phoneNumber:
|
|
30676
|
+
phoneNumber: z130.string().min(8).max(32).optional()
|
|
30244
30677
|
});
|
|
30245
|
-
var GroupedTaskSource =
|
|
30246
|
-
type:
|
|
30247
|
-
taskId:
|
|
30248
|
-
angleIndex:
|
|
30249
|
-
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([
|
|
30250
30683
|
"with_subtitles",
|
|
30251
30684
|
"without_subtitles"
|
|
30252
30685
|
]).optional(),
|
|
30253
|
-
imageVariantIndex:
|
|
30254
|
-
});
|
|
30255
|
-
var GroupedMediaSource =
|
|
30256
|
-
type:
|
|
30257
|
-
url:
|
|
30258
|
-
path:
|
|
30259
|
-
thumbnailUrl:
|
|
30260
|
-
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()
|
|
30261
30694
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
30262
30695
|
message: "media source needs exactly one of `url` or `path`"
|
|
30263
30696
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
30264
30697
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
30265
30698
|
});
|
|
30266
|
-
var GroupedUploadedAssetSource =
|
|
30267
|
-
type:
|
|
30699
|
+
var GroupedUploadedAssetSource = z130.object({
|
|
30700
|
+
type: z130.literal("asset"),
|
|
30268
30701
|
/** Returned by check_media_upload. ProductMaker resolves ownership server-side. */
|
|
30269
|
-
assetId:
|
|
30270
|
-
thumbnailAssetId:
|
|
30702
|
+
assetId: z130.string().uuid(),
|
|
30703
|
+
thumbnailAssetId: z130.string().uuid().optional()
|
|
30271
30704
|
});
|
|
30272
|
-
var GroupedCreative =
|
|
30273
|
-
type:
|
|
30705
|
+
var GroupedCreative = z130.object({
|
|
30706
|
+
type: z130.enum([
|
|
30274
30707
|
"image",
|
|
30275
30708
|
"video"
|
|
30276
30709
|
]),
|
|
30277
|
-
source:
|
|
30710
|
+
source: z130.union([
|
|
30278
30711
|
GroupedTaskSource,
|
|
30279
30712
|
GroupedMediaSource,
|
|
30280
30713
|
GroupedUploadedAssetSource
|
|
30281
30714
|
]),
|
|
30282
|
-
title:
|
|
30283
|
-
description:
|
|
30715
|
+
title: z130.string().max(120).optional(),
|
|
30716
|
+
description: z130.string().max(500).optional(),
|
|
30284
30717
|
cta: MetaCtaType.optional(),
|
|
30285
|
-
creativeId:
|
|
30718
|
+
creativeId: z130.string().min(1).optional()
|
|
30286
30719
|
});
|
|
30287
|
-
var GroupedCampaignConfigBase =
|
|
30720
|
+
var GroupedCampaignConfigBase = z130.object({
|
|
30288
30721
|
campaignType: MetaCampaignType.default("sales"),
|
|
30289
30722
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
30290
30723
|
budgetMode: MetaBudgetMode.optional(),
|
|
30291
|
-
campaignDailyBudget:
|
|
30292
|
-
adSets:
|
|
30293
|
-
tempId:
|
|
30294
|
-
name:
|
|
30295
|
-
dailyBudget:
|
|
30296
|
-
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)
|
|
30297
30730
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
30298
30731
|
/**
|
|
30299
30732
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -30301,17 +30734,17 @@ var GroupedCampaignConfigBase = z129.object({
|
|
|
30301
30734
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
30302
30735
|
* the API since #849 — unreachable from this tool.
|
|
30303
30736
|
*/
|
|
30304
|
-
destination:
|
|
30305
|
-
|
|
30306
|
-
type:
|
|
30307
|
-
landingUrl:
|
|
30737
|
+
destination: z130.discriminatedUnion("type", [
|
|
30738
|
+
z130.object({
|
|
30739
|
+
type: z130.literal("landing"),
|
|
30740
|
+
landingUrl: z130.string().url()
|
|
30308
30741
|
}),
|
|
30309
|
-
|
|
30310
|
-
type:
|
|
30311
|
-
phoneNumber:
|
|
30742
|
+
z130.object({
|
|
30743
|
+
type: z130.literal("whatsapp"),
|
|
30744
|
+
phoneNumber: z130.string().min(8).max(32)
|
|
30312
30745
|
})
|
|
30313
30746
|
]),
|
|
30314
|
-
saveAsDefault:
|
|
30747
|
+
saveAsDefault: z130.boolean().default(false),
|
|
30315
30748
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
30316
30749
|
});
|
|
30317
30750
|
function resolveBudgetMode(value) {
|
|
@@ -30325,7 +30758,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
30325
30758
|
const budgetMode = resolveBudgetMode(value);
|
|
30326
30759
|
if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
30327
30760
|
ctx.addIssue({
|
|
30328
|
-
code:
|
|
30761
|
+
code: z130.ZodIssueCode.custom,
|
|
30329
30762
|
path: [
|
|
30330
30763
|
"campaignDailyBudget"
|
|
30331
30764
|
],
|
|
@@ -30336,7 +30769,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
30336
30769
|
value.adSets.forEach((adSet, index) => {
|
|
30337
30770
|
if (adSet.dailyBudget === void 0) {
|
|
30338
30771
|
ctx.addIssue({
|
|
30339
|
-
code:
|
|
30772
|
+
code: z130.ZodIssueCode.custom,
|
|
30340
30773
|
path: [
|
|
30341
30774
|
"adSets",
|
|
30342
30775
|
index,
|
|
@@ -30351,25 +30784,25 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
30351
30784
|
...value,
|
|
30352
30785
|
budgetMode: resolveBudgetMode(value)
|
|
30353
30786
|
}));
|
|
30354
|
-
var PublishCreativesToMetaInputBase =
|
|
30355
|
-
creatives:
|
|
30787
|
+
var PublishCreativesToMetaInputBase = z130.object({
|
|
30788
|
+
creatives: z130.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
30356
30789
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
30357
|
-
portfolioId:
|
|
30790
|
+
portfolioId: z130.string().trim().min(1),
|
|
30358
30791
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
30359
|
-
pageId:
|
|
30792
|
+
pageId: z130.string().trim().min(1),
|
|
30360
30793
|
adAccountId: McpAdAccountId,
|
|
30361
|
-
pixelId:
|
|
30794
|
+
pixelId: z130.string().min(1).optional(),
|
|
30362
30795
|
/** Required unless `campaignConfig.destination` is whatsapp or a creative
|
|
30363
30796
|
* comes from a task (the API then lands on the published service landing
|
|
30364
30797
|
* of its service tasks) — see the superRefine below. Mirrors the API's
|
|
30365
30798
|
* `FacebookCreateCampaignFromTasksBodySchema`. */
|
|
30366
|
-
landingUrl:
|
|
30799
|
+
landingUrl: z130.string().url().optional(),
|
|
30367
30800
|
/** ISO-2 code or resolvable country name — mirrors the API contract
|
|
30368
30801
|
* (CountryIso2Schema), which normalizes names to alpha-2 at validation.
|
|
30369
30802
|
* Omitted: the brand country of its service tasks (the API answers
|
|
30370
30803
|
* META_NO_TARGET_COUNTRY when it has none); "US" for products. */
|
|
30371
30804
|
targetCountry: CountryIso2Schema.optional(),
|
|
30372
|
-
campaignName:
|
|
30805
|
+
campaignName: z130.string().trim().min(1).max(100),
|
|
30373
30806
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
30374
30807
|
});
|
|
30375
30808
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
@@ -30377,7 +30810,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
30377
30810
|
const hasTaskCreative = value.creatives.some((creative) => creative.source.type === "task");
|
|
30378
30811
|
if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl && !hasTaskCreative) {
|
|
30379
30812
|
ctx.addIssue({
|
|
30380
|
-
code:
|
|
30813
|
+
code: z130.ZodIssueCode.custom,
|
|
30381
30814
|
path: [
|
|
30382
30815
|
"landingUrl"
|
|
30383
30816
|
],
|
|
@@ -30386,7 +30819,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
30386
30819
|
}
|
|
30387
30820
|
if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
|
|
30388
30821
|
ctx.addIssue({
|
|
30389
|
-
code:
|
|
30822
|
+
code: z130.ZodIssueCode.custom,
|
|
30390
30823
|
path: [
|
|
30391
30824
|
"campaignConfig",
|
|
30392
30825
|
"destination",
|
|
@@ -30405,16 +30838,16 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
30405
30838
|
}
|
|
30406
30839
|
return value;
|
|
30407
30840
|
});
|
|
30408
|
-
var PresignUploadResponseSchema =
|
|
30409
|
-
uploadUrl:
|
|
30841
|
+
var PresignUploadResponseSchema = z130.object({
|
|
30842
|
+
uploadUrl: z130.string().url().refine((value) => {
|
|
30410
30843
|
const url = new URL(value);
|
|
30411
30844
|
return !url.username && !url.password && url.protocol === "https:";
|
|
30412
30845
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
30413
|
-
publicUrl:
|
|
30846
|
+
publicUrl: z130.string().url().refine((value) => {
|
|
30414
30847
|
const url = new URL(value);
|
|
30415
30848
|
return !url.username && !url.password;
|
|
30416
30849
|
}, "public media URL must not contain embedded credentials"),
|
|
30417
|
-
stagingAssetId:
|
|
30850
|
+
stagingAssetId: z130.string().uuid()
|
|
30418
30851
|
});
|
|
30419
30852
|
async function uploadResolvedMedia(api, media, assetRole) {
|
|
30420
30853
|
if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
|
|
@@ -30439,16 +30872,41 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
|
|
|
30439
30872
|
return uploadResolvedMedia(api, media, assetRole);
|
|
30440
30873
|
}
|
|
30441
30874
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
30442
|
-
var
|
|
30443
|
-
|
|
30444
|
-
|
|
30445
|
-
|
|
30446
|
-
|
|
30447
|
-
|
|
30448
|
-
|
|
30875
|
+
var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
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."),
|
|
30878
|
+
budgetCurrency: TikTokBudgetCurrencySchema.optional(),
|
|
30879
|
+
destination: TikTokCampaignDestinationSchema.optional()
|
|
30880
|
+
}).superRefine((value, ctx) => {
|
|
30881
|
+
if (value.dailyBudgetUsd !== void 0 && (value.dailyBudget !== void 0 || value.budgetCurrency !== void 0)) {
|
|
30882
|
+
ctx.addIssue({
|
|
30883
|
+
code: z130.ZodIssueCode.custom,
|
|
30884
|
+
path: [
|
|
30885
|
+
"dailyBudgetUsd"
|
|
30886
|
+
],
|
|
30887
|
+
message: "Choose either dailyBudgetUsd or dailyBudget with budgetCurrency, never both."
|
|
30888
|
+
});
|
|
30889
|
+
}
|
|
30890
|
+
if (value.dailyBudget !== void 0 !== (value.budgetCurrency !== void 0)) {
|
|
30891
|
+
ctx.addIssue({
|
|
30892
|
+
code: z130.ZodIssueCode.custom,
|
|
30893
|
+
path: [
|
|
30894
|
+
"budgetCurrency"
|
|
30895
|
+
],
|
|
30896
|
+
message: "dailyBudget and budgetCurrency must be supplied together."
|
|
30897
|
+
});
|
|
30898
|
+
}
|
|
30899
|
+
});
|
|
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(),
|
|
30906
|
+
campaignConfig: TikTokMcpCampaignConfig.optional(),
|
|
30449
30907
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
30450
30908
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
30451
|
-
primaryText:
|
|
30909
|
+
primaryText: z130.string().trim().min(1).max(500).optional()
|
|
30452
30910
|
});
|
|
30453
30911
|
function registerPublishTools(server, api, opts = {}) {
|
|
30454
30912
|
const transport = opts.transport ?? "http";
|
|
@@ -30670,7 +31128,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
30670
31128
|
}
|
|
30671
31129
|
});
|
|
30672
31130
|
server.registerTool("publish_to_tiktok", {
|
|
30673
|
-
description: publishToTikTokGuidance.summary + " La campa\xF1a se crea SIEMPRE en estado PAUSED \u2014 el usuario la activa manualmente en TikTok Ads Manager. Use `list_tiktok_advertisers` antes para un advertiserId v\xE1lido. `pixelId` opcional pero recomendado para tracking. `angleIndexes` (opcional): selecciona qu\xE9 \xE1ngulos de la tarea original publicar \u2014 ej. [0,2]; omitir = publicar todos, incluidas las variantes creativas generadas despu\xE9s. `campaignConfig` (opcional): `
|
|
31131
|
+
description: publishToTikTokGuidance.summary + " Las tareas de servicios publican \xFAnicamente videos en TikTok; para im\xE1genes de servicios usa Meta Ads. No omitas im\xE1genes seleccionadas de una tarea mixta para forzar su publicaci\xF3n. La campa\xF1a se crea SIEMPRE en estado PAUSED \u2014 el usuario la activa manualmente en TikTok Ads Manager. Use `list_tiktok_advertisers` antes para un advertiserId v\xE1lido. `pixelId` opcional pero recomendado para tracking web. `campaignConfig.destination` acepta landing (default) o {type: 'whatsapp', phoneNumber: '+573001234567', phoneRegionCode?: 'CO'} para anuncios nativos de WhatsApp con videos. WhatsApp usa clics y no requiere landingUrl ni pixelId; phoneRegionCode identifica el pa\xEDs del tel\xE9fono y es obligatorio si el prefijo es compartido, como +1. No asumas que coincide con el pa\xEDs objetivo. TikTok exige habilitaci\xF3n de mensajer\xEDa para algunos advertisers; no sustituyas una denegaci\xF3n por tr\xE1fico web. `angleIndexes` (opcional): selecciona qu\xE9 \xE1ngulos de la tarea original publicar \u2014 ej. [0,2]; omitir = publicar todos, incluidas las variantes creativas generadas despu\xE9s. `campaignConfig` (opcional): `dailyBudget` + `budgetCurrency` (importe diario en unidades principales de la moneda de la cuenta, ej. 25.50 y USD, o 20000 y COP). `dailyBudgetUsd` es legado y solo sirve para cuentas USD; no se convierte FX. No env\xEDes ambos. Sin presupuesto expl\xEDcito se usa el m\xEDnimo documentado para la moneda real de la cuenta. `targetCountries` (c\xF3digos ISO-2, primero se usa), `campaignName` (m\xE1x 100 chars). `primaryText` (opcional, m\xE1x 500 chars): override del copy del anuncio \u2014 solo \xFAsalo si el usuario te pas\xF3 un texto espec\xEDfico; si no, ProductMaker usa el copy auto-generado de la tarea. Despu\xE9s de publicar usa `get_tiktok_campaign_status` para chequear el estado.",
|
|
30674
31132
|
inputSchema: TikTokInput.shape,
|
|
30675
31133
|
annotations: CREATES_EXTERNAL
|
|
30676
31134
|
}, async ({ taskId, campaignConfig, primaryText, ...rest }) => {
|
|
@@ -30791,48 +31249,55 @@ function toMetaCampaignOverrides(cfg) {
|
|
|
30791
31249
|
__name(toMetaCampaignOverrides, "toMetaCampaignOverrides");
|
|
30792
31250
|
function toTikTokCampaignConfig(cfg) {
|
|
30793
31251
|
const out = {};
|
|
30794
|
-
if (cfg.dailyBudgetUsd !== void 0)
|
|
31252
|
+
if (cfg.dailyBudgetUsd !== void 0) {
|
|
31253
|
+
out.dailyBudget = cfg.dailyBudgetUsd;
|
|
31254
|
+
out.budgetCurrency = "USD";
|
|
31255
|
+
} else if (cfg.dailyBudget !== void 0) {
|
|
31256
|
+
out.dailyBudget = cfg.dailyBudget;
|
|
31257
|
+
out.budgetCurrency = cfg.budgetCurrency;
|
|
31258
|
+
}
|
|
30795
31259
|
if (cfg.targetCountries && cfg.targetCountries.length > 0) out.targetCountry = cfg.targetCountries[0];
|
|
30796
31260
|
if (cfg.campaignName !== void 0) out.campaignName = cfg.campaignName;
|
|
31261
|
+
if (cfg.destination !== void 0) out.destination = cfg.destination;
|
|
30797
31262
|
return out;
|
|
30798
31263
|
}
|
|
30799
31264
|
__name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
30800
31265
|
|
|
30801
31266
|
// src/tools/meta-upload.ts
|
|
30802
31267
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
30803
|
-
import { z as
|
|
31268
|
+
import { z as z131 } from "zod";
|
|
30804
31269
|
var requestMediaUploadGuidance = getProductMakerCapabilityModelGuidance("request_media_upload");
|
|
30805
|
-
var UploadSessionInputBase =
|
|
30806
|
-
mediaKind:
|
|
31270
|
+
var UploadSessionInputBase = z131.object({
|
|
31271
|
+
mediaKind: z131.enum([
|
|
30807
31272
|
"image",
|
|
30808
31273
|
"video",
|
|
30809
31274
|
"mixed"
|
|
30810
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."),
|
|
30811
|
-
assetRole:
|
|
31276
|
+
assetRole: z131.enum([
|
|
30812
31277
|
"media",
|
|
30813
31278
|
"thumbnail"
|
|
30814
31279
|
]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail."),
|
|
30815
|
-
uploadPurpose:
|
|
31280
|
+
uploadPurpose: z131.enum([
|
|
30816
31281
|
"product_reference",
|
|
30817
31282
|
"creative",
|
|
30818
31283
|
"generic"
|
|
30819
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."),
|
|
30820
|
-
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.")
|
|
30821
31286
|
});
|
|
30822
|
-
var OpenAiFileReference =
|
|
30823
|
-
download_url:
|
|
30824
|
-
file_id:
|
|
30825
|
-
mime_type:
|
|
30826
|
-
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()
|
|
30827
31292
|
});
|
|
30828
31293
|
var UploadSessionToolInputShape = {
|
|
30829
31294
|
...UploadSessionInputBase.shape,
|
|
30830
|
-
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.")
|
|
30831
31296
|
};
|
|
30832
|
-
var UploadSessionToolInput =
|
|
31297
|
+
var UploadSessionToolInput = z131.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
|
|
30833
31298
|
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
30834
31299
|
ctx.addIssue({
|
|
30835
|
-
code:
|
|
31300
|
+
code: z131.ZodIssueCode.custom,
|
|
30836
31301
|
path: [
|
|
30837
31302
|
"mediaKind"
|
|
30838
31303
|
],
|
|
@@ -30841,7 +31306,7 @@ var UploadSessionToolInput = z130.object(UploadSessionToolInputShape).superRefin
|
|
|
30841
31306
|
}
|
|
30842
31307
|
if (value.mediaKind === "video" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
|
|
30843
31308
|
ctx.addIssue({
|
|
30844
|
-
code:
|
|
31309
|
+
code: z131.ZodIssueCode.custom,
|
|
30845
31310
|
path: [
|
|
30846
31311
|
"maxFiles"
|
|
30847
31312
|
],
|
|
@@ -30850,7 +31315,7 @@ var UploadSessionToolInput = z130.object(UploadSessionToolInputShape).superRefin
|
|
|
30850
31315
|
}
|
|
30851
31316
|
if (value.mediaKind === "image" && (value.maxFiles ?? 1) > 5) {
|
|
30852
31317
|
ctx.addIssue({
|
|
30853
|
-
code:
|
|
31318
|
+
code: z131.ZodIssueCode.custom,
|
|
30854
31319
|
path: [
|
|
30855
31320
|
"maxFiles"
|
|
30856
31321
|
],
|
|
@@ -30858,38 +31323,38 @@ var UploadSessionToolInput = z130.object(UploadSessionToolInputShape).superRefin
|
|
|
30858
31323
|
});
|
|
30859
31324
|
}
|
|
30860
31325
|
});
|
|
30861
|
-
var UploadSessionResponse =
|
|
30862
|
-
sessionId:
|
|
30863
|
-
uploadPageUrl:
|
|
30864
|
-
mediaKind:
|
|
31326
|
+
var UploadSessionResponse = z131.object({
|
|
31327
|
+
sessionId: z131.string().uuid(),
|
|
31328
|
+
uploadPageUrl: z131.string().url(),
|
|
31329
|
+
mediaKind: z131.enum([
|
|
30865
31330
|
"image",
|
|
30866
31331
|
"video",
|
|
30867
31332
|
"mixed"
|
|
30868
31333
|
]),
|
|
30869
|
-
assetRole:
|
|
31334
|
+
assetRole: z131.enum([
|
|
30870
31335
|
"media",
|
|
30871
31336
|
"thumbnail"
|
|
30872
31337
|
]),
|
|
30873
|
-
maxFiles:
|
|
30874
|
-
expiresAt:
|
|
31338
|
+
maxFiles: z131.number().int().positive(),
|
|
31339
|
+
expiresAt: z131.string().datetime()
|
|
30875
31340
|
});
|
|
30876
|
-
var UploadStatusInput =
|
|
30877
|
-
sessionId:
|
|
31341
|
+
var UploadStatusInput = z131.object({
|
|
31342
|
+
sessionId: z131.string().uuid().describe("The sessionId returned by request_media_upload.")
|
|
30878
31343
|
});
|
|
30879
|
-
var UniversalUploadStatusResponse =
|
|
30880
|
-
|
|
30881
|
-
status:
|
|
30882
|
-
assets:
|
|
31344
|
+
var UniversalUploadStatusResponse = z131.discriminatedUnion("status", [
|
|
31345
|
+
z131.object({
|
|
31346
|
+
status: z131.literal("pending"),
|
|
31347
|
+
assets: z131.array(z131.never()).length(0)
|
|
30883
31348
|
}),
|
|
30884
|
-
|
|
30885
|
-
status:
|
|
30886
|
-
assets:
|
|
30887
|
-
assetId:
|
|
30888
|
-
mediaKind:
|
|
31349
|
+
z131.object({
|
|
31350
|
+
status: z131.literal("uploaded"),
|
|
31351
|
+
assets: z131.array(z131.object({
|
|
31352
|
+
assetId: z131.string().uuid(),
|
|
31353
|
+
mediaKind: z131.enum([
|
|
30889
31354
|
"image",
|
|
30890
31355
|
"video"
|
|
30891
31356
|
]),
|
|
30892
|
-
assetRole:
|
|
31357
|
+
assetRole: z131.enum([
|
|
30893
31358
|
"media",
|
|
30894
31359
|
"thumbnail"
|
|
30895
31360
|
])
|
|
@@ -30897,13 +31362,13 @@ var UniversalUploadStatusResponse = z130.discriminatedUnion("status", [
|
|
|
30897
31362
|
})
|
|
30898
31363
|
]);
|
|
30899
31364
|
var UniversalUploadStructuredResponse = UploadSessionResponse.extend({
|
|
30900
|
-
uploadPurpose:
|
|
31365
|
+
uploadPurpose: z131.enum([
|
|
30901
31366
|
"product_reference",
|
|
30902
31367
|
"creative",
|
|
30903
31368
|
"generic"
|
|
30904
31369
|
]),
|
|
30905
|
-
checkToolName:
|
|
30906
|
-
resolveEndpoint:
|
|
31370
|
+
checkToolName: z131.literal("check_media_upload"),
|
|
31371
|
+
resolveEndpoint: z131.string().url()
|
|
30907
31372
|
});
|
|
30908
31373
|
function contextualizeUploadPageUrl(uploadPageUrl, uploadPurpose) {
|
|
30909
31374
|
const url = new URL(uploadPageUrl);
|
|
@@ -30994,15 +31459,15 @@ function registerMediaUploadTools(server, api) {
|
|
|
30994
31459
|
__name(registerMediaUploadTools, "registerMediaUploadTools");
|
|
30995
31460
|
|
|
30996
31461
|
// src/tools/campaigns.ts
|
|
30997
|
-
import { z as
|
|
31462
|
+
import { z as z132 } from "zod";
|
|
30998
31463
|
var getMetaCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_meta_campaign_status");
|
|
30999
31464
|
var getTikTokCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_tiktok_campaign_status");
|
|
31000
|
-
var MetaStatusInput =
|
|
31001
|
-
campaignId:
|
|
31465
|
+
var MetaStatusInput = z132.object({
|
|
31466
|
+
campaignId: z132.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
31002
31467
|
});
|
|
31003
|
-
var TikTokStatusInput =
|
|
31004
|
-
campaignId:
|
|
31005
|
-
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.")
|
|
31006
31471
|
});
|
|
31007
31472
|
function registerCampaignTools(server, api) {
|
|
31008
31473
|
server.registerTool("get_meta_campaign_status", {
|
|
@@ -31107,10 +31572,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
31107
31572
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
31108
31573
|
|
|
31109
31574
|
// ../../packages/product-economics/src/product-costing.ts
|
|
31110
|
-
import { z as
|
|
31575
|
+
import { z as z134 } from "zod";
|
|
31111
31576
|
|
|
31112
31577
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
31113
|
-
import { z as
|
|
31578
|
+
import { z as z133 } from "zod";
|
|
31114
31579
|
|
|
31115
31580
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
31116
31581
|
var product_costing_shipping_references_default = {
|
|
@@ -31174,21 +31639,21 @@ var product_costing_shipping_references_default = {
|
|
|
31174
31639
|
};
|
|
31175
31640
|
|
|
31176
31641
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
31177
|
-
var ShippingReferenceSchema =
|
|
31178
|
-
reference:
|
|
31179
|
-
min:
|
|
31180
|
-
max:
|
|
31181
|
-
updatedAt:
|
|
31182
|
-
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([
|
|
31183
31648
|
"high",
|
|
31184
31649
|
"medium",
|
|
31185
31650
|
"medium-low"
|
|
31186
31651
|
]),
|
|
31187
|
-
sources:
|
|
31652
|
+
sources: z133.array(z133.string().url().startsWith("https://")).min(1).max(3)
|
|
31188
31653
|
}).superRefine(({ reference, min, max }, context) => {
|
|
31189
31654
|
if (min > reference || reference > max) {
|
|
31190
31655
|
context.addIssue({
|
|
31191
|
-
code:
|
|
31656
|
+
code: z133.ZodIssueCode.custom,
|
|
31192
31657
|
message: "shipping_reference_outside_range",
|
|
31193
31658
|
path: [
|
|
31194
31659
|
"reference"
|
|
@@ -31196,7 +31661,7 @@ var ShippingReferenceSchema = z132.object({
|
|
|
31196
31661
|
});
|
|
31197
31662
|
}
|
|
31198
31663
|
});
|
|
31199
|
-
var SHIPPING_REFERENCES =
|
|
31664
|
+
var SHIPPING_REFERENCES = z133.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
31200
31665
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
31201
31666
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
31202
31667
|
"MX",
|
|
@@ -31378,18 +31843,18 @@ function getProductCostingCountryName(country) {
|
|
|
31378
31843
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
31379
31844
|
|
|
31380
31845
|
// ../../packages/product-economics/src/product-costing.ts
|
|
31381
|
-
var ProductCostingInputSchema =
|
|
31382
|
-
country:
|
|
31383
|
-
supplierCost:
|
|
31384
|
-
shippingCost:
|
|
31385
|
-
returnRate:
|
|
31386
|
-
cancellationRate:
|
|
31387
|
-
adsManagerCpa:
|
|
31388
|
-
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")
|
|
31389
31854
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
31390
31855
|
if (returnRate + cancellationRate >= 1) {
|
|
31391
31856
|
context.addIssue({
|
|
31392
|
-
code:
|
|
31857
|
+
code: z134.ZodIssueCode.custom,
|
|
31393
31858
|
message: "delivery_rate_positive",
|
|
31394
31859
|
path: [
|
|
31395
31860
|
"cancellationRate"
|
|
@@ -31486,15 +31951,15 @@ function calculateProductCosting(rawInput) {
|
|
|
31486
31951
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
31487
31952
|
|
|
31488
31953
|
// src/tools/product-costing.ts
|
|
31489
|
-
import { z as
|
|
31954
|
+
import { z as z135 } from "zod";
|
|
31490
31955
|
var costProductGuidance = getProductMakerCapabilityModelGuidance("cost_product");
|
|
31491
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
31492
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
31493
|
-
var ProductCostingToolInput =
|
|
31494
|
-
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, {
|
|
31495
31960
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
31496
31961
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
31497
|
-
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."),
|
|
31498
31963
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
31499
31964
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
31500
31965
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -31657,7 +32122,7 @@ function formatPercent(value) {
|
|
|
31657
32122
|
__name(formatPercent, "formatPercent");
|
|
31658
32123
|
|
|
31659
32124
|
// src/tools/product-research.ts
|
|
31660
|
-
import { z as
|
|
32125
|
+
import { z as z136 } from "zod";
|
|
31661
32126
|
|
|
31662
32127
|
// src/tools/product-research-idempotency.ts
|
|
31663
32128
|
var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
|
|
@@ -32028,17 +32493,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
32028
32493
|
]);
|
|
32029
32494
|
var POLL_INTERVAL_MS = 5e3;
|
|
32030
32495
|
var MAX_WAIT_SECONDS = 120;
|
|
32031
|
-
var FindInput =
|
|
32496
|
+
var FindInput = z136.object({
|
|
32032
32497
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
32033
32498
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
32034
|
-
confirmed:
|
|
32035
|
-
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.")
|
|
32036
32501
|
});
|
|
32037
|
-
var GetInput =
|
|
32038
|
-
runId:
|
|
32502
|
+
var GetInput = z136.object({
|
|
32503
|
+
runId: z136.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
32039
32504
|
});
|
|
32040
|
-
var ListInput =
|
|
32041
|
-
limit:
|
|
32505
|
+
var ListInput = z136.object({
|
|
32506
|
+
limit: z136.number().int().min(1).max(20).default(10)
|
|
32042
32507
|
});
|
|
32043
32508
|
function textResult(text2) {
|
|
32044
32509
|
return {
|
|
@@ -32177,17 +32642,17 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
32177
32642
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
32178
32643
|
|
|
32179
32644
|
// src/tools/dropshipping-projects.ts
|
|
32180
|
-
import { z as
|
|
32645
|
+
import { z as z137 } from "zod";
|
|
32181
32646
|
var startDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("start_dropshipping_project");
|
|
32182
32647
|
var costDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("cost_dropshipping_project");
|
|
32183
32648
|
var guideDropshippingLaunchGuidance = getProductMakerCapabilityModelGuidance("guide_dropshipping_launch");
|
|
32184
|
-
var ProjectId =
|
|
32185
|
-
var DropshippingProjectStatus =
|
|
32649
|
+
var ProjectId = z137.string().uuid();
|
|
32650
|
+
var DropshippingProjectStatus = z137.enum([
|
|
32186
32651
|
"active",
|
|
32187
32652
|
"completed",
|
|
32188
32653
|
"archived"
|
|
32189
32654
|
]);
|
|
32190
|
-
var DropshippingProjectPhase =
|
|
32655
|
+
var DropshippingProjectPhase = z137.enum([
|
|
32191
32656
|
"research",
|
|
32192
32657
|
"costing",
|
|
32193
32658
|
"media",
|
|
@@ -32196,21 +32661,21 @@ var DropshippingProjectPhase = z136.enum([
|
|
|
32196
32661
|
"advertising",
|
|
32197
32662
|
"complete"
|
|
32198
32663
|
]);
|
|
32199
|
-
var DropshippingRewardMilestone =
|
|
32664
|
+
var DropshippingRewardMilestone = z137.enum([
|
|
32200
32665
|
"product_selected",
|
|
32201
32666
|
"costing_saved",
|
|
32202
32667
|
"product_media_ready",
|
|
32203
32668
|
"creatives_completed",
|
|
32204
32669
|
"campaign_created"
|
|
32205
32670
|
]);
|
|
32206
|
-
var TargetCountry =
|
|
32671
|
+
var TargetCountry = z137.enum([
|
|
32207
32672
|
"CO",
|
|
32208
32673
|
"MX",
|
|
32209
32674
|
"PE",
|
|
32210
32675
|
"EC",
|
|
32211
32676
|
"CL"
|
|
32212
32677
|
]);
|
|
32213
|
-
var ResearchCategory =
|
|
32678
|
+
var ResearchCategory = z137.enum([
|
|
32214
32679
|
"auto",
|
|
32215
32680
|
"automotive",
|
|
32216
32681
|
"kitchen",
|
|
@@ -32225,7 +32690,7 @@ var ResearchCategory = z136.enum([
|
|
|
32225
32690
|
"garden",
|
|
32226
32691
|
"cleaning"
|
|
32227
32692
|
]);
|
|
32228
|
-
var HttpUrl3 =
|
|
32693
|
+
var HttpUrl3 = z137.string().url().max(2e3).refine((value) => {
|
|
32229
32694
|
try {
|
|
32230
32695
|
return [
|
|
32231
32696
|
"http:",
|
|
@@ -32235,45 +32700,45 @@ var HttpUrl3 = z136.string().url().max(2e3).refine((value) => {
|
|
|
32235
32700
|
return false;
|
|
32236
32701
|
}
|
|
32237
32702
|
}, "URL must use HTTP(S)");
|
|
32238
|
-
var ResearchCandidateOrigin =
|
|
32239
|
-
type:
|
|
32703
|
+
var ResearchCandidateOrigin = z137.object({
|
|
32704
|
+
type: z137.literal("research_candidate"),
|
|
32240
32705
|
runId: ProjectId,
|
|
32241
|
-
candidateSourceId:
|
|
32706
|
+
candidateSourceId: z137.string().min(1).max(160)
|
|
32242
32707
|
});
|
|
32243
|
-
var TaskOrigin =
|
|
32244
|
-
type:
|
|
32708
|
+
var TaskOrigin = z137.object({
|
|
32709
|
+
type: z137.literal("task"),
|
|
32245
32710
|
taskId: ProjectId
|
|
32246
32711
|
});
|
|
32247
|
-
var ManualOrigin =
|
|
32248
|
-
type:
|
|
32712
|
+
var ManualOrigin = z137.object({
|
|
32713
|
+
type: z137.literal("manual"),
|
|
32249
32714
|
clientRequestId: ProjectId,
|
|
32250
|
-
country:
|
|
32715
|
+
country: z137.string().min(2).max(3).optional()
|
|
32251
32716
|
});
|
|
32252
|
-
var StartProjectInput =
|
|
32253
|
-
origin:
|
|
32717
|
+
var StartProjectInput = z137.object({
|
|
32718
|
+
origin: z137.discriminatedUnion("type", [
|
|
32254
32719
|
ResearchCandidateOrigin,
|
|
32255
32720
|
TaskOrigin,
|
|
32256
32721
|
ManualOrigin
|
|
32257
32722
|
]),
|
|
32258
|
-
productName:
|
|
32723
|
+
productName: z137.string().trim().min(1).max(240).optional(),
|
|
32259
32724
|
productUrl: HttpUrl3.optional(),
|
|
32260
|
-
country:
|
|
32261
|
-
languageCode:
|
|
32725
|
+
country: z137.string().min(2).max(3).optional(),
|
|
32726
|
+
languageCode: z137.string().min(2).max(16).optional()
|
|
32262
32727
|
}).strict();
|
|
32263
|
-
var CostProjectInput =
|
|
32728
|
+
var CostProjectInput = z137.object({
|
|
32264
32729
|
projectId: ProjectId,
|
|
32265
|
-
version:
|
|
32266
|
-
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, {
|
|
32267
32732
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
32268
32733
|
}).describe("C\xF3digo ISO-2 del pa\xEDs del proyecto. Usa el `targetCountry` que devuelve el proyecto."),
|
|
32269
|
-
supplierCost:
|
|
32270
|
-
shippingCost:
|
|
32271
|
-
returnRate:
|
|
32272
|
-
cancellationRate:
|
|
32273
|
-
adsManagerCpa:
|
|
32274
|
-
desiredMargin:
|
|
32275
|
-
});
|
|
32276
|
-
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({
|
|
32277
32742
|
projectId: ProjectId.optional()
|
|
32278
32743
|
}).strict();
|
|
32279
32744
|
var NEXT_ACTION_KINDS = [
|
|
@@ -32291,29 +32756,29 @@ var NEXT_ACTION_KINDS = [
|
|
|
32291
32756
|
"repair_meta_campaign",
|
|
32292
32757
|
"review_paused_campaign"
|
|
32293
32758
|
];
|
|
32294
|
-
var DropshippingProjectNextAction =
|
|
32295
|
-
kind:
|
|
32296
|
-
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)
|
|
32297
32762
|
})));
|
|
32298
|
-
var CostingInputSchema =
|
|
32299
|
-
country:
|
|
32300
|
-
supplierCost:
|
|
32301
|
-
shippingCost:
|
|
32302
|
-
returnRate:
|
|
32303
|
-
cancellationRate:
|
|
32304
|
-
adsManagerCpa:
|
|
32305
|
-
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)
|
|
32306
32771
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
32307
32772
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
32308
|
-
code:
|
|
32773
|
+
code: z137.ZodIssueCode.custom,
|
|
32309
32774
|
path: [
|
|
32310
32775
|
"cancellationRate"
|
|
32311
32776
|
]
|
|
32312
32777
|
});
|
|
32313
32778
|
});
|
|
32314
|
-
var FiniteCostingNumber =
|
|
32315
|
-
var CostingResultSchema =
|
|
32316
|
-
country:
|
|
32779
|
+
var FiniteCostingNumber = z137.number().finite();
|
|
32780
|
+
var CostingResultSchema = z137.object({
|
|
32781
|
+
country: z137.string().min(2).max(3),
|
|
32317
32782
|
deliveryRate: FiniteCostingNumber,
|
|
32318
32783
|
adjustedShippingCost: FiniteCostingNumber,
|
|
32319
32784
|
adjustedCpa: FiniteCostingNumber,
|
|
@@ -32326,45 +32791,45 @@ var CostingResultSchema = z136.object({
|
|
|
32326
32791
|
estimatedProfit: FiniteCostingNumber,
|
|
32327
32792
|
actualMargin: FiniteCostingNumber,
|
|
32328
32793
|
breakEvenAdsManagerCpa: FiniteCostingNumber,
|
|
32329
|
-
wasPriceAutoAdjusted:
|
|
32330
|
-
profitabilityStatus:
|
|
32331
|
-
});
|
|
32332
|
-
var RewardProgressSchema =
|
|
32333
|
-
campaignKey:
|
|
32334
|
-
totalCredits:
|
|
32335
|
-
earnedCredits:
|
|
32336
|
-
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({
|
|
32337
32802
|
milestone: DropshippingRewardMilestone,
|
|
32338
|
-
credits:
|
|
32339
|
-
available:
|
|
32340
|
-
earned:
|
|
32803
|
+
credits: z137.number().int().positive(),
|
|
32804
|
+
available: z137.boolean().optional(),
|
|
32805
|
+
earned: z137.boolean(),
|
|
32341
32806
|
grantId: ProjectId.nullable(),
|
|
32342
|
-
grantedAt:
|
|
32807
|
+
grantedAt: z137.string().datetime().nullable()
|
|
32343
32808
|
})).length(DropshippingRewardMilestone.options.length)
|
|
32344
32809
|
});
|
|
32345
|
-
var ProjectResponseSchema =
|
|
32810
|
+
var ProjectResponseSchema = z137.object({
|
|
32346
32811
|
id: ProjectId,
|
|
32347
32812
|
taskId: ProjectId.nullable(),
|
|
32348
32813
|
status: DropshippingProjectStatus,
|
|
32349
|
-
originKey:
|
|
32814
|
+
originKey: z137.string().min(1),
|
|
32350
32815
|
researchRunId: ProjectId.nullable(),
|
|
32351
|
-
candidateSourceId:
|
|
32352
|
-
productName:
|
|
32816
|
+
candidateSourceId: z137.string().nullable(),
|
|
32817
|
+
productName: z137.string().nullable(),
|
|
32353
32818
|
productUrl: HttpUrl3.nullable(),
|
|
32354
32819
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
32355
|
-
targetCountry:
|
|
32356
|
-
languageCode:
|
|
32357
|
-
hasReferenceImage:
|
|
32358
|
-
hasWinnerReferenceImage:
|
|
32820
|
+
targetCountry: z137.string().nullable(),
|
|
32821
|
+
languageCode: z137.string().nullable(),
|
|
32822
|
+
hasReferenceImage: z137.boolean(),
|
|
32823
|
+
hasWinnerReferenceImage: z137.boolean(),
|
|
32359
32824
|
costingInput: CostingInputSchema.nullable(),
|
|
32360
32825
|
costingResult: CostingResultSchema.nullable(),
|
|
32361
|
-
costingVersion:
|
|
32362
|
-
version:
|
|
32826
|
+
costingVersion: z137.number().int().positive().nullable(),
|
|
32827
|
+
version: z137.number().int().positive(),
|
|
32363
32828
|
phase: DropshippingProjectPhase,
|
|
32364
32829
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
32365
32830
|
rewards: RewardProgressSchema,
|
|
32366
|
-
createdAt:
|
|
32367
|
-
updatedAt:
|
|
32831
|
+
createdAt: z137.string().datetime(),
|
|
32832
|
+
updatedAt: z137.string().datetime()
|
|
32368
32833
|
}).passthrough();
|
|
32369
32834
|
var NEXT_ACTION_GUIDANCE = {
|
|
32370
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.",
|
|
@@ -32484,58 +32949,58 @@ function registerDropshippingProjectTools(server, api) {
|
|
|
32484
32949
|
__name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
|
|
32485
32950
|
|
|
32486
32951
|
// src/tools/daily-control.ts
|
|
32487
|
-
import { z as
|
|
32952
|
+
import { z as z138 } from "zod";
|
|
32488
32953
|
var getDailyControlGuidance = getProductMakerCapabilityModelGuidance("get_daily_control");
|
|
32489
32954
|
var linkCampaignToProjectGuidance = getProductMakerCapabilityModelGuidance("link_campaign_to_project");
|
|
32490
32955
|
var reportDailyReturnsGuidance = getProductMakerCapabilityModelGuidance("report_daily_returns");
|
|
32491
|
-
var ProviderCampaignIdentifier =
|
|
32492
|
-
var DateOnly =
|
|
32493
|
-
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({
|
|
32494
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."),
|
|
32495
|
-
days:
|
|
32496
|
-
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.")
|
|
32497
32962
|
}).strict();
|
|
32498
|
-
var LinkCampaignInput =
|
|
32499
|
-
projectId:
|
|
32500
|
-
provider:
|
|
32963
|
+
var LinkCampaignInput = z138.object({
|
|
32964
|
+
projectId: z138.string().uuid(),
|
|
32965
|
+
provider: z138.enum([
|
|
32501
32966
|
"meta",
|
|
32502
32967
|
"tiktok"
|
|
32503
32968
|
]),
|
|
32504
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."),
|
|
32505
32970
|
campaignId: ProviderCampaignIdentifier
|
|
32506
32971
|
}).strict();
|
|
32507
|
-
var ReportReturnsInput =
|
|
32508
|
-
projectId:
|
|
32972
|
+
var ReportReturnsInput = z138.object({
|
|
32973
|
+
projectId: z138.string().uuid(),
|
|
32509
32974
|
date: DateOnly,
|
|
32510
|
-
returnedOrders:
|
|
32511
|
-
note:
|
|
32975
|
+
returnedOrders: z138.number().int().min(0).max(1e5),
|
|
32976
|
+
note: z138.string().trim().max(280).optional()
|
|
32512
32977
|
}).strict();
|
|
32513
|
-
var DailyControlStatusMirror = DailyControlStatusEnum.or(
|
|
32514
|
-
var DailyControlLightMirror = DailyControlLightEnum.or(
|
|
32515
|
-
var DailyControlSourceMirror = DailyControlSourceEnum.or(
|
|
32516
|
-
var DailyControlVerdictMirror = DailyControlVerdictEnum.or(
|
|
32517
|
-
var DailyControlDayMirror =
|
|
32518
|
-
date:
|
|
32519
|
-
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(),
|
|
32520
32985
|
returnedSource: DailyControlSourceMirror,
|
|
32521
32986
|
cancelledSource: DailyControlSourceMirror,
|
|
32522
|
-
hasEstimates:
|
|
32987
|
+
hasEstimates: z138.boolean(),
|
|
32523
32988
|
light: DailyControlLightMirror
|
|
32524
32989
|
}).passthrough();
|
|
32525
|
-
var DailyControlProductMirror =
|
|
32526
|
-
projectId:
|
|
32527
|
-
productName:
|
|
32990
|
+
var DailyControlProductMirror = z138.object({
|
|
32991
|
+
projectId: z138.string().uuid(),
|
|
32992
|
+
productName: z138.string().nullable(),
|
|
32528
32993
|
status: DailyControlStatusMirror,
|
|
32529
|
-
statusMessage:
|
|
32530
|
-
days:
|
|
32994
|
+
statusMessage: z138.string().nullable(),
|
|
32995
|
+
days: z138.array(DailyControlDayMirror),
|
|
32531
32996
|
verdict: DailyControlVerdictMirror.nullable(),
|
|
32532
|
-
rationale:
|
|
32997
|
+
rationale: z138.string().nullable()
|
|
32533
32998
|
}).passthrough();
|
|
32534
|
-
var DailyControlResponseMirror =
|
|
32535
|
-
since:
|
|
32536
|
-
until:
|
|
32537
|
-
products:
|
|
32538
|
-
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())
|
|
32539
33004
|
}).passthrough();
|
|
32540
33005
|
var MCP_WARNINGS_KEY = "_mcpDailyControlWarnings";
|
|
32541
33006
|
function withWarnings(response) {
|
|
@@ -32610,7 +33075,7 @@ __name(registerDailyControlTools, "registerDailyControlTools");
|
|
|
32610
33075
|
// src/tools/product-control.ts
|
|
32611
33076
|
import { readFile, stat } from "fs/promises";
|
|
32612
33077
|
import { basename as basename2, isAbsolute, resolve as resolve3 } from "path";
|
|
32613
|
-
import { z as
|
|
33078
|
+
import { z as z139 } from "zod";
|
|
32614
33079
|
var getProductControlGuidance = getProductMakerCapabilityModelGuidance("get_product_control");
|
|
32615
33080
|
var listImportableProductsGuidance = getProductMakerCapabilityModelGuidance("list_importable_products");
|
|
32616
33081
|
var importProductControlsGuidance = getProductMakerCapabilityModelGuidance("import_product_controls");
|
|
@@ -32623,36 +33088,36 @@ var createOperationGuidance = getProductMakerCapabilityModelGuidance("create_ope
|
|
|
32623
33088
|
var mergeProductsGuidance = getProductMakerCapabilityModelGuidance("merge_products");
|
|
32624
33089
|
var syncShopifyOrdersGuidance = getProductMakerCapabilityModelGuidance("sync_shopify_orders");
|
|
32625
33090
|
var importOrdersFileGuidance = getProductMakerCapabilityModelGuidance("import_orders_file");
|
|
32626
|
-
var DateOnly2 =
|
|
33091
|
+
var DateOnly2 = z139.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
32627
33092
|
var IMPORT_BATCH_MAX2 = 50;
|
|
32628
|
-
var ProductControlListInput =
|
|
32629
|
-
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."),
|
|
32630
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."),
|
|
32631
|
-
shopDomain:
|
|
33096
|
+
shopDomain: z139.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
|
|
32632
33097
|
}).strict();
|
|
32633
|
-
var ImportableProductsInput =
|
|
32634
|
-
q:
|
|
32635
|
-
after:
|
|
32636
|
-
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()
|
|
32637
33102
|
}).strict();
|
|
32638
|
-
var ImportProductControlsInput =
|
|
32639
|
-
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."),
|
|
32640
33105
|
// Obligatorio, como en el endpoint: con dos tiendas conectadas, importar
|
|
32641
33106
|
// sin decir a cuál archivaría el producto bajo la tienda equivocada.
|
|
32642
|
-
shopDomain:
|
|
33107
|
+
shopDomain: z139.string().min(1)
|
|
32643
33108
|
}).strict();
|
|
32644
|
-
var SetProductCostingInput =
|
|
32645
|
-
shopDomain:
|
|
32646
|
-
shopifyProductId:
|
|
32647
|
-
targetCountry:
|
|
32648
|
-
supplierCost:
|
|
32649
|
-
shippingCost:
|
|
32650
|
-
returnRate:
|
|
32651
|
-
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)
|
|
32652
33117
|
}).strict();
|
|
32653
|
-
var Count =
|
|
32654
|
-
var ReportProductCountsInput =
|
|
32655
|
-
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."),
|
|
32656
33121
|
date: DateOnly2,
|
|
32657
33122
|
orders: Count.describe("Pedidos que entraron ese d\xEDa."),
|
|
32658
33123
|
cancelledOrders: Count,
|
|
@@ -32662,36 +33127,36 @@ var ReportProductCountsInput = z138.object({
|
|
|
32662
33127
|
}).strict();
|
|
32663
33128
|
var MAX_ORDERS_FILE_BYTES = 8 * 1024 * 1024;
|
|
32664
33129
|
var SyncShopifyOrdersInput = SyncShopifyOrdersSchema;
|
|
32665
|
-
var ImportOrdersFileInput =
|
|
33130
|
+
var ImportOrdersFileInput = z139.object({
|
|
32666
33131
|
/** Ruta ABSOLUTA en la máquina de quien usa el MCP. */
|
|
32667
|
-
path:
|
|
32668
|
-
operationId:
|
|
33132
|
+
path: z139.string().min(1),
|
|
33133
|
+
operationId: z139.string().uuid().optional(),
|
|
32669
33134
|
/** Solo mirar: devuelve el resumen y no escribe nada. */
|
|
32670
|
-
dryRun:
|
|
33135
|
+
dryRun: z139.boolean().optional()
|
|
32671
33136
|
}).strict();
|
|
32672
|
-
var CreateOperationInput =
|
|
32673
|
-
countryCode:
|
|
32674
|
-
label:
|
|
33137
|
+
var CreateOperationInput = z139.object({
|
|
33138
|
+
countryCode: z139.string().length(2),
|
|
33139
|
+
label: z139.string().trim().min(1).max(60).optional()
|
|
32675
33140
|
}).strict();
|
|
32676
|
-
var MergeProductsInput =
|
|
32677
|
-
sourceId:
|
|
32678
|
-
targetId:
|
|
33141
|
+
var MergeProductsInput = z139.object({
|
|
33142
|
+
sourceId: z139.string().uuid(),
|
|
33143
|
+
targetId: z139.string().uuid()
|
|
32679
33144
|
}).strict();
|
|
32680
|
-
var SetMonthlyExpenseInput =
|
|
32681
|
-
month:
|
|
33145
|
+
var SetMonthlyExpenseInput = z139.object({
|
|
33146
|
+
month: z139.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
|
|
32682
33147
|
label: SaveMonthlyExpenseSchema.shape.label,
|
|
32683
|
-
amount:
|
|
32684
|
-
operationId:
|
|
33148
|
+
amount: z139.number().finite().min(0),
|
|
33149
|
+
operationId: z139.string().uuid().optional()
|
|
32685
33150
|
}).strict();
|
|
32686
|
-
var SetAdSpendInput =
|
|
32687
|
-
month:
|
|
32688
|
-
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(),
|
|
32689
33154
|
/** null RETIRA la corrección; cero afirma que no pautó. */
|
|
32690
|
-
amount:
|
|
33155
|
+
amount: z139.number().finite().min(0).nullable()
|
|
32691
33156
|
}).strict();
|
|
32692
|
-
var GetFinancesInput =
|
|
32693
|
-
month:
|
|
32694
|
-
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.")
|
|
32695
33160
|
}).strict();
|
|
32696
33161
|
function registerProductControlTools(server, api, { transport = "http" } = {}) {
|
|
32697
33162
|
server.registerTool("get_product_control", {
|
|
@@ -32883,28 +33348,28 @@ function registerProductControlTools(server, api, { transport = "http" } = {}) {
|
|
|
32883
33348
|
__name(registerProductControlTools, "registerProductControlTools");
|
|
32884
33349
|
|
|
32885
33350
|
// src/tools/market-study-estimate.ts
|
|
32886
|
-
import { z as
|
|
33351
|
+
import { z as z140 } from "zod";
|
|
32887
33352
|
var estimateMarketStudyGuidance = getProductMakerCapabilityModelGuidance("estimate_market_study_cost");
|
|
32888
|
-
var ProductTaskOutputEnum =
|
|
33353
|
+
var ProductTaskOutputEnum = z140.enum([
|
|
32889
33354
|
"landing",
|
|
32890
33355
|
"video",
|
|
32891
33356
|
"image"
|
|
32892
33357
|
]);
|
|
32893
|
-
var EstimateMarketStudyInput =
|
|
32894
|
-
kind:
|
|
33358
|
+
var EstimateMarketStudyInput = z140.object({
|
|
33359
|
+
kind: z140.enum([
|
|
32895
33360
|
"video",
|
|
32896
33361
|
"product_task"
|
|
32897
33362
|
]).describe('"video" para generate_video_creative; "product_task" para create_product_task.'),
|
|
32898
|
-
videoAngleCount:
|
|
32899
|
-
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."),
|
|
32900
33365
|
videoStructure: VideoStructureEnum.default("single_take"),
|
|
32901
33366
|
videoQuality: VideoQualityEnum.default("economy"),
|
|
32902
33367
|
videoResolution: VideoResolutionEnum.default("480p"),
|
|
32903
|
-
productImageCount:
|
|
33368
|
+
productImageCount: z140.number().int().min(1).max(5).optional().describe("Cantidad de fotos del producto que adjuntar\xE1s (1-5)."),
|
|
32904
33369
|
outputs: ProductTaskOutputEnum.array().min(1).max(3).optional().describe('Solo para kind="product_task": los mismos outputs de create_product_task. Default: todos.'),
|
|
32905
|
-
generateAiMusic:
|
|
33370
|
+
generateAiMusic: z140.boolean().optional(),
|
|
32906
33371
|
generateAiEdit: AiEditRequestSchema.optional(),
|
|
32907
|
-
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.")
|
|
32908
33373
|
}).strict();
|
|
32909
33374
|
function buildMarketStudyEstimateRequests(input) {
|
|
32910
33375
|
const quotesVideo = input.kind === "video" || (input.outputs ?? [
|