@productmaker/mcp 8.1.0 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/http.js +1940 -861
- package/dist/stdio.js +1885 -836
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -68,6 +68,23 @@ var VideoPreset = z.enum([
|
|
|
68
68
|
]);
|
|
69
69
|
var VoiceId = z.string();
|
|
70
70
|
var _VIDEO_MODEL_SET = new Set(VideoModel.options);
|
|
71
|
+
var BROLL_VIDEO_MODES = new Set(VideoModel.options);
|
|
72
|
+
var PersistedVideoMode = z.enum([
|
|
73
|
+
...VideoModel.options,
|
|
74
|
+
"avatar_talking",
|
|
75
|
+
"hybrid",
|
|
76
|
+
"sora"
|
|
77
|
+
]);
|
|
78
|
+
var PersistedVideoModel = z.enum([
|
|
79
|
+
...VideoModel.options,
|
|
80
|
+
"sora",
|
|
81
|
+
"infinitalk"
|
|
82
|
+
]);
|
|
83
|
+
var PLAN_VIDEO_MODES = [
|
|
84
|
+
...VideoModel.options,
|
|
85
|
+
"avatar_talking",
|
|
86
|
+
"hybrid"
|
|
87
|
+
];
|
|
71
88
|
|
|
72
89
|
// ../../packages/types/src/video-creation.ts
|
|
73
90
|
import { z as z3 } from "zod";
|
|
@@ -1038,40 +1055,206 @@ var VideoTier = z8.preprocess((v) => v === "grok" ? "single_call" : v, z8.enum([
|
|
|
1038
1055
|
"single_call"
|
|
1039
1056
|
]));
|
|
1040
1057
|
var DEFAULT_VIDEO_TIER = "single_call";
|
|
1058
|
+
var WritableVideoTier = VideoTier.refine((tier) => tier !== "legacy", {
|
|
1059
|
+
message: "VIDEO_TIER_RETIRED"
|
|
1060
|
+
});
|
|
1041
1061
|
|
|
1042
|
-
// ../../packages/types/src/image-
|
|
1043
|
-
var
|
|
1044
|
-
"
|
|
1045
|
-
"
|
|
1062
|
+
// ../../packages/types/src/image-models.ts
|
|
1063
|
+
var IMAGE_RESOLUTIONS = [
|
|
1064
|
+
"1k",
|
|
1065
|
+
"2k",
|
|
1066
|
+
"4k"
|
|
1046
1067
|
];
|
|
1047
|
-
var
|
|
1048
|
-
"
|
|
1049
|
-
"5:
|
|
1050
|
-
"
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
"
|
|
1068
|
+
var GPT_IMAGE_2_5_TOKENS = {
|
|
1069
|
+
"1k": "gpt-image-2-5:1k",
|
|
1070
|
+
"2k": "gpt-image-2-5:2k",
|
|
1071
|
+
"4k": "gpt-image-2-5:4k"
|
|
1072
|
+
};
|
|
1073
|
+
var GPT_IMAGE_2_TOKENS = {
|
|
1074
|
+
"1k": "gpt-image-2:1k",
|
|
1075
|
+
"2k": "gpt-image-2:2k",
|
|
1076
|
+
"4k": "gpt-image-2:4k"
|
|
1077
|
+
};
|
|
1078
|
+
var GPT_IMAGE_2_5 = {
|
|
1079
|
+
id: "gpt-image-2.5",
|
|
1080
|
+
status: "active",
|
|
1081
|
+
renderedBy: "gpt-image-2.5",
|
|
1082
|
+
tokens: GPT_IMAGE_2_5_TOKENS,
|
|
1083
|
+
kie: {
|
|
1084
|
+
imageToImage: [
|
|
1085
|
+
"gpt-image-2-5-sunburst-image-to-image",
|
|
1086
|
+
"gpt-image-2-5-flare-image-to-image"
|
|
1087
|
+
],
|
|
1088
|
+
textToImage: [
|
|
1089
|
+
"gpt-image-2-5-sunburst-text-to-image",
|
|
1090
|
+
"gpt-image-2-5-flare-text-to-image"
|
|
1091
|
+
],
|
|
1092
|
+
shorthands: [
|
|
1093
|
+
"gpt-image-2-5"
|
|
1094
|
+
]
|
|
1095
|
+
},
|
|
1096
|
+
fal: {
|
|
1097
|
+
edit: "openai/gpt-image-2.5/sunburst/edit",
|
|
1098
|
+
textToImage: "openai/gpt-image-2.5/sunburst/text-to-image",
|
|
1099
|
+
quality: {
|
|
1100
|
+
"1k": "low",
|
|
1101
|
+
"2k": "medium",
|
|
1102
|
+
"4k": "high"
|
|
1103
|
+
},
|
|
1104
|
+
ourCostCents: {
|
|
1105
|
+
"1k": 1.5,
|
|
1106
|
+
"2k": 6.1,
|
|
1107
|
+
"4k": 17.8
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
// Docs KIE de gpt-image-2-5 (sunburst/flare): 27:16, 16:27, 9:8, 8:9 solo 1K;
|
|
1111
|
+
// 4:5 y 5:4 también bloqueados por encima de 1K.
|
|
1112
|
+
ratiosBlockedAbove1k: [
|
|
1113
|
+
"4:5",
|
|
1114
|
+
"5:4",
|
|
1115
|
+
"27:16",
|
|
1116
|
+
"16:27",
|
|
1117
|
+
"9:8",
|
|
1118
|
+
"8:9"
|
|
1119
|
+
],
|
|
1120
|
+
ledgerSuffix: {
|
|
1121
|
+
"1k": "gpt-image-2",
|
|
1122
|
+
"2k": "gpt-image-2:medium",
|
|
1123
|
+
"4k": "gpt-image-2:high"
|
|
1124
|
+
},
|
|
1125
|
+
falLedgerSuffix: {
|
|
1126
|
+
"1k": "gpt-image-2:1k",
|
|
1127
|
+
"2k": "gpt-image-2:2k",
|
|
1128
|
+
"4k": "gpt-image-2:4k"
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
var GPT_IMAGE_2 = {
|
|
1132
|
+
...GPT_IMAGE_2_5,
|
|
1133
|
+
id: "gpt-image-2",
|
|
1134
|
+
status: "retired",
|
|
1135
|
+
renderedBy: GPT_IMAGE_2_5.id,
|
|
1136
|
+
tokens: GPT_IMAGE_2_TOKENS,
|
|
1137
|
+
kie: {
|
|
1138
|
+
imageToImage: [
|
|
1139
|
+
"gpt-image-2-image-to-image"
|
|
1140
|
+
],
|
|
1141
|
+
textToImage: [
|
|
1142
|
+
"gpt-image-2-text-to-image"
|
|
1143
|
+
],
|
|
1144
|
+
shorthands: [
|
|
1145
|
+
"gpt-image-2"
|
|
1146
|
+
]
|
|
1147
|
+
},
|
|
1148
|
+
ratiosBlockedAbove1k: [
|
|
1149
|
+
"4:5",
|
|
1150
|
+
"5:4"
|
|
1151
|
+
]
|
|
1152
|
+
};
|
|
1153
|
+
var IMAGE_MODELS = [
|
|
1154
|
+
GPT_IMAGE_2_5,
|
|
1155
|
+
GPT_IMAGE_2
|
|
1054
1156
|
];
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1157
|
+
var ACTIVE_IMAGE_MODEL = GPT_IMAGE_2_5;
|
|
1158
|
+
var IMAGE_WRITE_TOKENS = GPT_IMAGE_2_5_TOKENS;
|
|
1159
|
+
var LEGACY_IMAGE_TOKENS = GPT_IMAGE_2_TOKENS;
|
|
1160
|
+
var PUBLIC_IMAGE_QUALITY_INPUTS = [
|
|
1161
|
+
IMAGE_WRITE_TOKENS["1k"],
|
|
1162
|
+
IMAGE_WRITE_TOKENS["2k"],
|
|
1163
|
+
LEGACY_IMAGE_TOKENS["1k"],
|
|
1164
|
+
LEGACY_IMAGE_TOKENS["2k"],
|
|
1165
|
+
"image:1k",
|
|
1166
|
+
"image:2k"
|
|
1167
|
+
];
|
|
1168
|
+
function entryById(id) {
|
|
1169
|
+
const found = IMAGE_MODELS.find((entry) => entry.id === id);
|
|
1170
|
+
if (!found) throw new Error(`IMAGE_MODEL_UNKNOWN:${id}`);
|
|
1171
|
+
return found;
|
|
1172
|
+
}
|
|
1173
|
+
__name(entryById, "entryById");
|
|
1174
|
+
function resolveImageToken(raw) {
|
|
1175
|
+
if (typeof raw !== "string") return null;
|
|
1176
|
+
let normalized = raw.trim().toLowerCase();
|
|
1177
|
+
let viaFal = false;
|
|
1178
|
+
if (normalized.startsWith("fal:")) {
|
|
1179
|
+
viaFal = true;
|
|
1180
|
+
normalized = normalized.slice("fal:".length);
|
|
1181
|
+
}
|
|
1182
|
+
for (const resolution of IMAGE_RESOLUTIONS) {
|
|
1183
|
+
if (normalized === `image:${resolution}`) {
|
|
1184
|
+
return {
|
|
1185
|
+
resolution,
|
|
1186
|
+
model: ACTIVE_IMAGE_MODEL,
|
|
1187
|
+
declared: ACTIVE_IMAGE_MODEL,
|
|
1188
|
+
spelling: "intent",
|
|
1189
|
+
viaFal
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1059
1192
|
}
|
|
1060
|
-
|
|
1193
|
+
for (const entry of IMAGE_MODELS) {
|
|
1194
|
+
for (const resolution of IMAGE_RESOLUTIONS) {
|
|
1195
|
+
if (entry.tokens[resolution] === normalized) {
|
|
1196
|
+
return {
|
|
1197
|
+
resolution,
|
|
1198
|
+
model: entryById(entry.renderedBy),
|
|
1199
|
+
declared: entry,
|
|
1200
|
+
spelling: entry.status === "active" ? "current" : "retired",
|
|
1201
|
+
viaFal
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1208
|
+
__name(resolveImageToken, "resolveImageToken");
|
|
1209
|
+
function imageResolutionOf(raw) {
|
|
1210
|
+
const token = resolveImageToken(raw);
|
|
1211
|
+
return token && !token.viaFal ? token.resolution : null;
|
|
1212
|
+
}
|
|
1213
|
+
__name(imageResolutionOf, "imageResolutionOf");
|
|
1214
|
+
function requireKieImageId(ids, index, label) {
|
|
1215
|
+
const id = ids[index];
|
|
1216
|
+
if (!id) throw new Error(`IMAGE_MODEL_NO_KIE_ID:${label}`);
|
|
1217
|
+
return id;
|
|
1218
|
+
}
|
|
1219
|
+
__name(requireKieImageId, "requireKieImageId");
|
|
1220
|
+
var KIE_FLARE_IMAGE_TO_IMAGE_MODEL = requireKieImageId(GPT_IMAGE_2_5.kie.imageToImage, 1, "flare-imageToImage");
|
|
1221
|
+
function canonicalImageModelInput(raw) {
|
|
1222
|
+
if (raw === void 0) return void 0;
|
|
1223
|
+
const token = resolveImageToken(raw);
|
|
1224
|
+
if (!token || token.viaFal || token.spelling !== "intent") return raw;
|
|
1225
|
+
return IMAGE_WRITE_TOKENS[token.resolution];
|
|
1226
|
+
}
|
|
1227
|
+
__name(canonicalImageModelInput, "canonicalImageModelInput");
|
|
1228
|
+
function imageTokenSupportsRatio(aspectRatio, rawToken) {
|
|
1229
|
+
const token = resolveImageToken(rawToken);
|
|
1230
|
+
if (!token) return true;
|
|
1231
|
+
if (token.resolution === "1k") return true;
|
|
1232
|
+
return !token.declared.ratiosBlockedAbove1k.includes(aspectRatio);
|
|
1233
|
+
}
|
|
1234
|
+
__name(imageTokenSupportsRatio, "imageTokenSupportsRatio");
|
|
1235
|
+
|
|
1236
|
+
// ../../packages/types/src/image-provider.ts
|
|
1237
|
+
var GPT_IMAGE_2_RATIOS_BLOCKED_ABOVE_1K = GPT_IMAGE_2.ratiosBlockedAbove1k;
|
|
1238
|
+
var GPT_IMAGE_2_5_RATIOS_BLOCKED_ABOVE_1K = GPT_IMAGE_2_5.ratiosBlockedAbove1k;
|
|
1239
|
+
function gptImage2SupportsTier(aspectRatio, tier) {
|
|
1240
|
+
return imageTokenSupportsRatio(aspectRatio, tier);
|
|
1061
1241
|
}
|
|
1062
1242
|
__name(gptImage2SupportsTier, "gptImage2SupportsTier");
|
|
1063
1243
|
var IMAGE_TIER_UNSUPPORTED_FOR_RATIO_RULE = "image_tier_unsupported_for_ratio";
|
|
1064
1244
|
var IMAGE_TIER_LIST = [
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
"gpt-image-2-5:4k",
|
|
1068
|
-
"gpt-image-2:1k",
|
|
1069
|
-
"gpt-image-2:2k",
|
|
1070
|
-
"gpt-image-2:4k"
|
|
1245
|
+
...Object.values(IMAGE_WRITE_TOKENS),
|
|
1246
|
+
...Object.values(LEGACY_IMAGE_TOKENS)
|
|
1071
1247
|
];
|
|
1248
|
+
function isImageTier(value) {
|
|
1249
|
+
return IMAGE_TIER_LIST.includes(value);
|
|
1250
|
+
}
|
|
1251
|
+
__name(isImageTier, "isImageTier");
|
|
1072
1252
|
function parseImageTier(imageModel) {
|
|
1073
|
-
const
|
|
1074
|
-
|
|
1253
|
+
const token = resolveImageToken(imageModel);
|
|
1254
|
+
if (!token || token.viaFal) return null;
|
|
1255
|
+
if (token.spelling === "intent") return IMAGE_WRITE_TOKENS[token.resolution];
|
|
1256
|
+
const declared = token.declared.tokens[token.resolution];
|
|
1257
|
+
return isImageTier(declared) ? declared : null;
|
|
1075
1258
|
}
|
|
1076
1259
|
__name(parseImageTier, "parseImageTier");
|
|
1077
1260
|
function validateImageTierForRatio(aspectRatio, imageModel) {
|
|
@@ -1647,6 +1830,9 @@ var VideoCreativeJobStatus = z14.object({
|
|
|
1647
1830
|
var VideoCreativeJobPayload = z14.object({
|
|
1648
1831
|
jobId: z14.string().optional(),
|
|
1649
1832
|
status: VideoCreativeJobStatusEnum.optional(),
|
|
1833
|
+
/** True when the submission replayed an existing task instead of billing
|
|
1834
|
+
* new work — callers surface the twin verbatim. Omitted on fresh creates. */
|
|
1835
|
+
replayed: z14.boolean().optional(),
|
|
1650
1836
|
videoStructure: VideoStructure.optional(),
|
|
1651
1837
|
videoQuality: VideoQuality.optional(),
|
|
1652
1838
|
videoRouteId: VideoRouteId.optional(),
|
|
@@ -1740,7 +1926,8 @@ var ImageCreativeJobStatus = z14.object({
|
|
|
1740
1926
|
});
|
|
1741
1927
|
var ImageCreativeJobPayload = z14.object({
|
|
1742
1928
|
jobId: z14.string().uuid(),
|
|
1743
|
-
status: ImageCreativeJobStatusEnum
|
|
1929
|
+
status: ImageCreativeJobStatusEnum,
|
|
1930
|
+
replayed: z14.boolean().optional()
|
|
1744
1931
|
});
|
|
1745
1932
|
var ImageCreativeHistoryEntry = z14.object({
|
|
1746
1933
|
id: z14.string().uuid(),
|
|
@@ -1767,7 +1954,8 @@ var LandingCreativeJobStatus = z14.object({
|
|
|
1767
1954
|
});
|
|
1768
1955
|
var LandingCreativeJobPayload = z14.object({
|
|
1769
1956
|
jobId: z14.string().uuid(),
|
|
1770
|
-
status: LandingCreativeJobStatusEnum
|
|
1957
|
+
status: LandingCreativeJobStatusEnum,
|
|
1958
|
+
replayed: z14.boolean().optional()
|
|
1771
1959
|
});
|
|
1772
1960
|
var LandingCreativeHistoryEntry = z14.object({
|
|
1773
1961
|
id: z14.string().uuid(),
|
|
@@ -3808,7 +3996,7 @@ var IngestOptions = z22.object({
|
|
|
3808
3996
|
durationSeconds: FreeVideoOptionsSchema.shape.durationSeconds.optional(),
|
|
3809
3997
|
addSubtitles: FreeVideoOptionsSchema.shape.addSubtitles.optional(),
|
|
3810
3998
|
subtitleText: z22.string().transform((value) => value.trim()).pipe(z22.string().max(FREE_VIDEO_SUBTITLE_MAX_CHARS)).optional(),
|
|
3811
|
-
videoTier:
|
|
3999
|
+
videoTier: WritableVideoTier.default(DEFAULT_VIDEO_TIER),
|
|
3812
4000
|
narrativeStyle: NarrativeStyle.default("review"),
|
|
3813
4001
|
videoPreset: VideoPreset.optional(),
|
|
3814
4002
|
multiAngleVideos: z22.boolean().default(false),
|
|
@@ -3882,7 +4070,9 @@ var IngestOptions = z22.object({
|
|
|
3882
4070
|
aspectRatio: z22.string().optional(),
|
|
3883
4071
|
brandColors: z22.string().optional(),
|
|
3884
4072
|
nVariants: z22.number().int().min(1).max(6).optional(),
|
|
3885
|
-
|
|
4073
|
+
// O7: `image:Nk` is accepted and stored as the spelling written today
|
|
4074
|
+
// (IMAGE_WRITE_TOKENS); see packages/types/src/image-models.ts.
|
|
4075
|
+
imageModel: z22.string().optional().transform((value) => canonicalImageModelInput(value)),
|
|
3886
4076
|
styleMix: z22.string().optional(),
|
|
3887
4077
|
aiDecidesAll: z22.boolean().optional(),
|
|
3888
4078
|
/** Opt out of the competitor-spy analysis. When false the worker skips
|
|
@@ -4001,7 +4191,7 @@ var VideoOptionsOverride = z22.object({
|
|
|
4001
4191
|
videoStructure: VideoStructure.optional(),
|
|
4002
4192
|
videoQuality: VideoQuality.optional(),
|
|
4003
4193
|
videoModel: VideoModel.optional(),
|
|
4004
|
-
videoTier:
|
|
4194
|
+
videoTier: WritableVideoTier.optional(),
|
|
4005
4195
|
ugcFormat: VideoFormat.optional(),
|
|
4006
4196
|
videoStyle: VideoStyle.optional(),
|
|
4007
4197
|
narrativeStyle: NarrativeStyle.optional(),
|
|
@@ -4084,6 +4274,11 @@ var IngestResumePayload = z22.object({
|
|
|
4084
4274
|
// "keep what the task has". See angle-management.service.ts resumeAfterAngles.
|
|
4085
4275
|
videoStyle: VideoStyle.optional(),
|
|
4086
4276
|
videoModel: VideoModel.default("veo"),
|
|
4277
|
+
// READ schema on purpose, unlike IngestOptions/VideoOptionsOverride: resume
|
|
4278
|
+
// does not write the tier — `resumeAfterAngles` projects the route from the
|
|
4279
|
+
// task's OWN persisted tier and ignores this field. MCP and WhatsApp echo
|
|
4280
|
+
// `status.videoTier`, which is still `legacy` for historical rows; refusing
|
|
4281
|
+
// it made those tasks impossible to resume from both channels.
|
|
4087
4282
|
videoTier: VideoTier.optional(),
|
|
4088
4283
|
videoStructure: VideoStructure.optional(),
|
|
4089
4284
|
videoQuality: VideoQuality.optional(),
|
|
@@ -4894,17 +5089,7 @@ var VideoGenerator = z26.object({
|
|
|
4894
5089
|
format: VideoFormat.optional(),
|
|
4895
5090
|
videoStyle: VideoStyle.optional(),
|
|
4896
5091
|
narrativeStyle: NarrativeStyle.optional(),
|
|
4897
|
-
videoMode:
|
|
4898
|
-
"veo",
|
|
4899
|
-
"wan",
|
|
4900
|
-
"bytedance",
|
|
4901
|
-
"seedance",
|
|
4902
|
-
"grok",
|
|
4903
|
-
"kling",
|
|
4904
|
-
"avatar_talking",
|
|
4905
|
-
"hybrid",
|
|
4906
|
-
"sora"
|
|
4907
|
-
]).optional(),
|
|
5092
|
+
videoMode: PersistedVideoMode.optional(),
|
|
4908
5093
|
videoResolution: VideoResolution.optional(),
|
|
4909
5094
|
videoStructure: VideoStructure.optional(),
|
|
4910
5095
|
videoQuality: VideoQuality.optional(),
|
|
@@ -5441,6 +5626,10 @@ var CreateShopifySubscriptionRequest = z28.object({
|
|
|
5441
5626
|
var CreateShopifyBillingChargeRequest = z28.object({
|
|
5442
5627
|
packId: z28.string().min(1)
|
|
5443
5628
|
});
|
|
5629
|
+
var ShopifyBillingCallbackQuery = z28.object({
|
|
5630
|
+
charge_id: z28.string().regex(/^\d+$/),
|
|
5631
|
+
transactionId: z28.string().uuid()
|
|
5632
|
+
});
|
|
5444
5633
|
var CancelShopifySubscriptionRequest = z28.object({
|
|
5445
5634
|
subscriptionId: z28.string().min(1)
|
|
5446
5635
|
});
|
|
@@ -7269,7 +7458,8 @@ var IMAGE_QA_GATES = [
|
|
|
7269
7458
|
"limb",
|
|
7270
7459
|
"device_panel",
|
|
7271
7460
|
"critical_feature",
|
|
7272
|
-
"cross_section"
|
|
7461
|
+
"cross_section",
|
|
7462
|
+
"usage_fidelity"
|
|
7273
7463
|
];
|
|
7274
7464
|
var ImageQaGate = z36.enum(IMAGE_QA_GATES);
|
|
7275
7465
|
var ImageQaGateOutcome = z36.enum([
|
|
@@ -8315,6 +8505,10 @@ var GEMINI_OMNI_FLAT_USER_PRICE_CENTS = 168;
|
|
|
8315
8505
|
// ../../packages/types/src/pricing.ts
|
|
8316
8506
|
var CREDITS_PER_USD_CENT = 10;
|
|
8317
8507
|
var CREDITS_PER_DOLLAR = 100 * CREDITS_PER_USD_CENT;
|
|
8508
|
+
var AD_CREATIVE_MODELS = {
|
|
8509
|
+
STANDARD: IMAGE_WRITE_TOKENS["1k"],
|
|
8510
|
+
PREMIUM: IMAGE_WRITE_TOKENS["2k"]
|
|
8511
|
+
};
|
|
8318
8512
|
var KALODATA_USD_CENTS_PER_PROVIDER_CREDIT = 9;
|
|
8319
8513
|
var KALODATA_PROVIDER_CREDITS_PER_SOURCE_CEILING = 7.5;
|
|
8320
8514
|
var DEFAULT_PRICING = {
|
|
@@ -9273,6 +9467,27 @@ var DEFAULT_PRICING = {
|
|
|
9273
9467
|
ourCostCents: 1.88,
|
|
9274
9468
|
userPriceCents: 3.76
|
|
9275
9469
|
},
|
|
9470
|
+
// Usage decider (role `usage-decider`: Gemini 3.8 Flash effort:'high' since
|
|
9471
|
+
// 05f543eae, ≤6 images + seller text, temperature 0 json_object, once per
|
|
9472
|
+
// classic landing, always on). Was 3¢ measured on Claude
|
|
9473
|
+
// Sonnet 5 (spike 2026-09-24, 17 prod tasks). NOT re-measured on Flash — no
|
|
9474
|
+
// local BillingEntry row carries the Flash decider yet — so it is priced as
|
|
9475
|
+
// the product truth (`openrouter:product-truth`, 2.2¢ measured from
|
|
9476
|
+
// OpenRouter usage.cost): same model, same effort, same ≤6-photo input and a
|
|
9477
|
+
// JSON answer of similar size. Refine from usage.cost after the e2e. Fallback
|
|
9478
|
+
// only (OpenRouter's usage.cost is billed when present).
|
|
9479
|
+
"openrouter:usage-decision": {
|
|
9480
|
+
ourCostCents: 2.2,
|
|
9481
|
+
userPriceCents: 4.4
|
|
9482
|
+
},
|
|
9483
|
+
// Usage-fidelity QA judge (records misuse; regen gated separately behind
|
|
9484
|
+
// always on, bounded by USAGE_FIDELITY_MAX_REGENS). Sized off landing-image-qa beside it: same
|
|
9485
|
+
// shape of call (1-2 images, a verdict), so quoted at the same rate until
|
|
9486
|
+
// it has its own BillingEntry volume to re-derive from.
|
|
9487
|
+
"openrouter:usage-fidelity": {
|
|
9488
|
+
ourCostCents: 1.88,
|
|
9489
|
+
userPriceCents: 3.76
|
|
9490
|
+
},
|
|
9276
9491
|
// Landing commercial-facts extraction (gemini-3.8-flash, ONE text-only call over
|
|
9277
9492
|
// the seller's offer/context/features). Sized off the image-QA key beside it,
|
|
9278
9493
|
// which measured 1.88¢ at ~11.1K prompt tokens: this prompt is capped at ~3K in
|
|
@@ -10011,6 +10226,24 @@ var DEFAULT_PRICING = {
|
|
|
10011
10226
|
ourCostCents: 0.2,
|
|
10012
10227
|
userPriceCents: 0.4
|
|
10013
10228
|
},
|
|
10229
|
+
// Product truth (Gemini 3.8 Flash effort:'high', ≤6 photos + all seller text, once per
|
|
10230
|
+
// extract, always on). Plan C spike 2026-09-24: 6 calls, US$0.132 total ⇒
|
|
10231
|
+
// 2.2¢/call measured from OpenRouter usage.cost. Replaces usage spec + functional demo +
|
|
10232
|
+
// product-use sheet (+ landing visual-context-fusion) on the same task. Fallback only.
|
|
10233
|
+
"openrouter:product-truth": {
|
|
10234
|
+
ourCostCents: 2.2,
|
|
10235
|
+
userPriceCents: 4.4
|
|
10236
|
+
},
|
|
10237
|
+
// Landing creative director, «categorías» arm (Gemini 3.8 Flash effort:'high', ≤6 photos +
|
|
10238
|
+
// all seller text + the section plan, once per classic landing behind
|
|
10239
|
+
// LANDING_DIRECTOR_ENABLED). ESTIMATE, not measured: same call shape as the product truth
|
|
10240
|
+
// (2.2¢) with a ~2-3x longer JSON answer (one content spec per section) ⇒ ~4¢. Replaces
|
|
10241
|
+
// the visual brief (4¢) + visual-context fusion (0.4¢) + usage decision (2.2¢) on that
|
|
10242
|
+
// landing. Refine from OpenRouter usage.cost after the e2e. Fallback only.
|
|
10243
|
+
"openrouter:landing-director": {
|
|
10244
|
+
ourCostCents: 4,
|
|
10245
|
+
userPriceCents: 8
|
|
10246
|
+
},
|
|
10014
10247
|
// Unit = one reference-grouping pass over the user's product images (0.1241¢).
|
|
10015
10248
|
"openrouter:product-reference-grouping": {
|
|
10016
10249
|
ourCostCents: 0.15,
|
|
@@ -10624,6 +10857,7 @@ var MixSceneResult = z48.object({
|
|
|
10624
10857
|
index: z48.number().int().positive(),
|
|
10625
10858
|
status: z48.enum([
|
|
10626
10859
|
"queued",
|
|
10860
|
+
"submitting",
|
|
10627
10861
|
"submitted",
|
|
10628
10862
|
"done",
|
|
10629
10863
|
"failed"
|
|
@@ -13222,7 +13456,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
13222
13456
|
], [
|
|
13223
13457
|
"product_costing"
|
|
13224
13458
|
]),
|
|
13225
|
-
defineCapability("create_creative_variant", "
|
|
13459
|
+
defineCapability("create_creative_variant", "3e02399a7785df328f1c4d63cfbb6d7481abaac9f91d4a5cf51d410e657d63aa", "credit_consuming", "creative", [
|
|
13226
13460
|
"credit_consuming",
|
|
13227
13461
|
"state_change"
|
|
13228
13462
|
], [
|
|
@@ -13472,7 +13706,9 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
13472
13706
|
// MCP 7.0.0 (Task 11, .agent/plans/2026-09-15-model-agnostic-video-routes.md):
|
|
13473
13707
|
// `freeProModel` retired in favor of `modelId` (registry ids). Recomputed
|
|
13474
13708
|
// from `pnpm --filter @productmaker/mcp test` printing the real digest.
|
|
13475
|
-
|
|
13709
|
+
// Re-baselined 2026-09-26: `idempotencyKey` field description corrected —
|
|
13710
|
+
// paid tools ignore caller-provided keys and derive their own semantic key.
|
|
13711
|
+
"35616a32a42a0bc062218fdfbb4cc7f4ecfa814571fb86b4f11ef9e4184855e1",
|
|
13476
13712
|
"credit_consuming",
|
|
13477
13713
|
"creative",
|
|
13478
13714
|
[
|
|
@@ -13528,7 +13764,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
13528
13764
|
"video"
|
|
13529
13765
|
]
|
|
13530
13766
|
),
|
|
13531
|
-
defineCapability("generate_image_creatives", "
|
|
13767
|
+
defineCapability("generate_image_creatives", "2f1698b623d98645b7c618a17988f3aac30f403a9c3f3dc26eb5bdbd9c6ef23e", "credit_consuming", "creative", [
|
|
13532
13768
|
"credit_consuming",
|
|
13533
13769
|
"state_change"
|
|
13534
13770
|
], [
|
|
@@ -15179,79 +15415,696 @@ var WhatsAppPresentationManifestSchema = z67.object({
|
|
|
15179
15415
|
}
|
|
15180
15416
|
});
|
|
15181
15417
|
|
|
15182
|
-
// ../../packages/types/src/
|
|
15418
|
+
// ../../packages/types/src/usage-decision.ts
|
|
15183
15419
|
import { z as z68 } from "zod";
|
|
15184
|
-
|
|
15420
|
+
|
|
15421
|
+
// ../../packages/types/src/text-clamp.ts
|
|
15422
|
+
function clampAtWordBoundary(value, max) {
|
|
15423
|
+
if (typeof value !== "string") return value;
|
|
15424
|
+
if (value.length <= max) return value;
|
|
15425
|
+
const cut = value.slice(0, max);
|
|
15426
|
+
const lastSpace = cut.lastIndexOf(" ");
|
|
15427
|
+
const atWordBoundary = lastSpace > 0 ? cut.slice(0, lastSpace) : cut;
|
|
15428
|
+
return atWordBoundary.replace(/[,;:\s]+$/, "");
|
|
15429
|
+
}
|
|
15430
|
+
__name(clampAtWordBoundary, "clampAtWordBoundary");
|
|
15431
|
+
function clampStringArray(max) {
|
|
15432
|
+
return (value) => {
|
|
15433
|
+
if (!Array.isArray(value)) return value;
|
|
15434
|
+
const nonEmpty = value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
15435
|
+
return nonEmpty.slice(0, max);
|
|
15436
|
+
};
|
|
15437
|
+
}
|
|
15438
|
+
__name(clampStringArray, "clampStringArray");
|
|
15439
|
+
|
|
15440
|
+
// ../../packages/types/src/usage-decision.ts
|
|
15441
|
+
var USAGE_SCENE_RULE_MAX_CHARS = 240;
|
|
15442
|
+
var USAGE_DECISION_MAX_REQUIRED_PARTS = 6;
|
|
15443
|
+
var USAGE_DECISION_MAX_ALTERNATE_USES = 4;
|
|
15444
|
+
var USAGE_DECISION_MAX_MISUSE = 5;
|
|
15445
|
+
function clampRule(value) {
|
|
15446
|
+
return clampAtWordBoundary(value, USAGE_SCENE_RULE_MAX_CHARS);
|
|
15447
|
+
}
|
|
15448
|
+
__name(clampRule, "clampRule");
|
|
15449
|
+
var UsageDecisionSchema = z68.object({
|
|
15450
|
+
productIdentity: z68.string().min(1),
|
|
15451
|
+
endUser: z68.object({
|
|
15452
|
+
who: z68.string(),
|
|
15453
|
+
appliedTo: z68.string()
|
|
15454
|
+
}),
|
|
15455
|
+
promise: z68.string().min(1),
|
|
15456
|
+
primaryUse: z68.object({
|
|
15457
|
+
scene: z68.string().min(1),
|
|
15458
|
+
postureAndGrip: z68.string(),
|
|
15459
|
+
contactPoint: z68.string(),
|
|
15460
|
+
requiredParts: z68.preprocess(clampStringArray(USAGE_DECISION_MAX_REQUIRED_PARTS), z68.array(z68.string()).max(USAGE_DECISION_MAX_REQUIRED_PARTS))
|
|
15461
|
+
}),
|
|
15462
|
+
validAlternateUses: z68.preprocess(clampStringArray(USAGE_DECISION_MAX_ALTERNATE_USES), z68.array(z68.string()).max(USAGE_DECISION_MAX_ALTERNATE_USES)),
|
|
15463
|
+
misuse: z68.preprocess(clampStringArray(USAGE_DECISION_MAX_MISUSE), z68.array(z68.string().min(1)).max(USAGE_DECISION_MAX_MISUSE)),
|
|
15464
|
+
useSceneRule: z68.preprocess(clampRule, z68.string().min(1).max(USAGE_SCENE_RULE_MAX_CHARS)),
|
|
15465
|
+
/**
|
|
15466
|
+
* True when the promise can ONLY be shown with the primary use (the pain is bending over and
|
|
15467
|
+
* the product has an extension pole: without the pole there is no promise); false when the
|
|
15468
|
+
* valid alternates sell it equally. Drives whether an alternate use in a use scene is a
|
|
15469
|
+
* failure (`requirePrimary` in the usage judge) and whether the prompts' primary use is
|
|
15470
|
+
* mandatory. Optional and fail-safe: missing (decisions stored before the field) or not a
|
|
15471
|
+
* boolean ⇒ `undefined`, read as false by every caller — never a rejected decision.
|
|
15472
|
+
*/
|
|
15473
|
+
primaryRequired: z68.boolean().optional().catch(void 0),
|
|
15474
|
+
confidence: z68.number().min(0).max(1),
|
|
15475
|
+
modelId: z68.string(),
|
|
15476
|
+
decidedAt: z68.string()
|
|
15477
|
+
});
|
|
15478
|
+
|
|
15479
|
+
// ../../packages/types/src/product-truth.ts
|
|
15480
|
+
import { z as z70 } from "zod";
|
|
15481
|
+
|
|
15482
|
+
// ../../packages/types/src/lenient-zod.ts
|
|
15483
|
+
import { z as z69 } from "zod";
|
|
15484
|
+
function trimAndClamp(value, max) {
|
|
15485
|
+
if (typeof value !== "string") return value;
|
|
15486
|
+
return clampAtWordBoundary(value.replace(/\s+/g, " ").trim(), max);
|
|
15487
|
+
}
|
|
15488
|
+
__name(trimAndClamp, "trimAndClamp");
|
|
15489
|
+
function lenientText(max) {
|
|
15490
|
+
return z69.preprocess((v) => trimAndClamp(v, max), z69.string().min(1).max(max));
|
|
15491
|
+
}
|
|
15492
|
+
__name(lenientText, "lenientText");
|
|
15493
|
+
function lenientOptionalText(max) {
|
|
15494
|
+
return z69.preprocess((v) => {
|
|
15495
|
+
if (typeof v !== "string") return void 0;
|
|
15496
|
+
const clamped = trimAndClamp(v, max);
|
|
15497
|
+
return typeof clamped === "string" && clamped.length > 0 ? clamped : void 0;
|
|
15498
|
+
}, z69.string().max(max).optional());
|
|
15499
|
+
}
|
|
15500
|
+
__name(lenientOptionalText, "lenientOptionalText");
|
|
15501
|
+
function lenientTextList(maxItems, maxChars) {
|
|
15502
|
+
return z69.preprocess((v) => {
|
|
15503
|
+
if (!Array.isArray(v)) return [];
|
|
15504
|
+
return v.map((item) => trimAndClamp(item, maxChars)).filter((item) => typeof item === "string" && item.length > 0).slice(0, maxItems);
|
|
15505
|
+
}, z69.array(z69.string().min(1).max(maxChars)).max(maxItems));
|
|
15506
|
+
}
|
|
15507
|
+
__name(lenientTextList, "lenientTextList");
|
|
15508
|
+
function lenientList(item, maxItems) {
|
|
15509
|
+
return z69.preprocess((v) => {
|
|
15510
|
+
if (!Array.isArray(v)) return [];
|
|
15511
|
+
return v.filter((entry) => item.safeParse(entry).success).slice(0, maxItems);
|
|
15512
|
+
}, z69.array(item).max(maxItems));
|
|
15513
|
+
}
|
|
15514
|
+
__name(lenientList, "lenientList");
|
|
15515
|
+
|
|
15516
|
+
// ../../packages/types/src/product-truth.ts
|
|
15517
|
+
var PRODUCT_TRUTH_SCHEMA_VERSION = 1;
|
|
15518
|
+
var PRODUCT_TRUTH_CAPS = {
|
|
15519
|
+
whatItIs: 160,
|
|
15520
|
+
deliveryForm: 120,
|
|
15521
|
+
distinctiveMarks: 6,
|
|
15522
|
+
distinctiveMarkChars: 160,
|
|
15523
|
+
parts: 12,
|
|
15524
|
+
partNameChars: 80,
|
|
15525
|
+
criticalFeatures: 6,
|
|
15526
|
+
connections: 6,
|
|
15527
|
+
connectionChars: 80,
|
|
15528
|
+
states: 4,
|
|
15529
|
+
stateKeyChars: 60,
|
|
15530
|
+
stateLabelChars: 80,
|
|
15531
|
+
partsPresent: 10,
|
|
15532
|
+
orientationInUseChars: 200,
|
|
15533
|
+
appliedToChars: 120,
|
|
15534
|
+
environmentChars: 120,
|
|
15535
|
+
summaryChars: 200,
|
|
15536
|
+
sceneChars: 300,
|
|
15537
|
+
postureAndGripChars: 160,
|
|
15538
|
+
contactPointChars: 120,
|
|
15539
|
+
steps: 8,
|
|
15540
|
+
stepNameChars: 120,
|
|
15541
|
+
stepInstructionChars: 300,
|
|
15542
|
+
visibleEffectChars: 200,
|
|
15543
|
+
beforeStateChars: 200,
|
|
15544
|
+
requiredProps: 8,
|
|
15545
|
+
requiredPropChars: 120,
|
|
15546
|
+
alternates: 3,
|
|
15547
|
+
misuse: 5,
|
|
15548
|
+
misuseChars: 200,
|
|
15549
|
+
mustShowFeatures: 5,
|
|
15550
|
+
mustShowFeatureChars: 100,
|
|
15551
|
+
safety: 5,
|
|
15552
|
+
safetyChars: 200,
|
|
15553
|
+
forbiddenClaims: 8,
|
|
15554
|
+
unsupportedClaims: 5,
|
|
15555
|
+
claimChars: 200,
|
|
15556
|
+
visualSummaryEnChars: 400
|
|
15557
|
+
};
|
|
15558
|
+
var PRODUCT_TRUTH_END_USER_AGE_BANDS = [
|
|
15559
|
+
"baby",
|
|
15560
|
+
"child",
|
|
15561
|
+
"teen",
|
|
15562
|
+
"adult",
|
|
15563
|
+
"any"
|
|
15564
|
+
];
|
|
15565
|
+
var PRODUCT_TRUTH_END_USER_GENDERS = [
|
|
15566
|
+
"female",
|
|
15567
|
+
"male",
|
|
15568
|
+
"any"
|
|
15569
|
+
];
|
|
15570
|
+
var PRODUCT_TRUTH_UNKNOWN_REASONS = [
|
|
15571
|
+
"model_unsure",
|
|
15572
|
+
"no_signal",
|
|
15573
|
+
"invalid_response",
|
|
15574
|
+
"provider_error"
|
|
15575
|
+
];
|
|
15576
|
+
var text = lenientText;
|
|
15577
|
+
var optionalText = lenientOptionalText;
|
|
15578
|
+
var textList = lenientTextList;
|
|
15579
|
+
function toProductTruthStateKey(value) {
|
|
15580
|
+
if (typeof value !== "string") return void 0;
|
|
15581
|
+
const slug = value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, PRODUCT_TRUTH_CAPS.stateKeyChars);
|
|
15582
|
+
return slug.length > 0 ? slug : void 0;
|
|
15583
|
+
}
|
|
15584
|
+
__name(toProductTruthStateKey, "toProductTruthStateKey");
|
|
15585
|
+
var stateKey = z70.preprocess(toProductTruthStateKey, z70.string().min(1).max(PRODUCT_TRUTH_CAPS.stateKeyChars));
|
|
15586
|
+
var optionalStateKey = z70.preprocess(toProductTruthStateKey, z70.string().min(1).max(PRODUCT_TRUTH_CAPS.stateKeyChars).optional());
|
|
15587
|
+
var AGE_BAND_ALIASES = {
|
|
15588
|
+
baby: "baby",
|
|
15589
|
+
babies: "baby",
|
|
15590
|
+
infant: "baby",
|
|
15591
|
+
infants: "baby",
|
|
15592
|
+
toddler: "child",
|
|
15593
|
+
child: "child",
|
|
15594
|
+
children: "child",
|
|
15595
|
+
kid: "child",
|
|
15596
|
+
kids: "child",
|
|
15597
|
+
teen: "teen",
|
|
15598
|
+
teens: "teen",
|
|
15599
|
+
teenager: "teen",
|
|
15600
|
+
teenagers: "teen",
|
|
15601
|
+
adult: "adult",
|
|
15602
|
+
adults: "adult",
|
|
15603
|
+
any: "any",
|
|
15604
|
+
all: "any",
|
|
15605
|
+
"all ages": "any"
|
|
15606
|
+
};
|
|
15607
|
+
var GENDER_ALIASES = {
|
|
15608
|
+
female: "female",
|
|
15609
|
+
woman: "female",
|
|
15610
|
+
women: "female",
|
|
15611
|
+
girl: "female",
|
|
15612
|
+
male: "male",
|
|
15613
|
+
man: "male",
|
|
15614
|
+
men: "male",
|
|
15615
|
+
boy: "male",
|
|
15616
|
+
any: "any",
|
|
15617
|
+
unisex: "any",
|
|
15618
|
+
both: "any",
|
|
15619
|
+
all: "any"
|
|
15620
|
+
};
|
|
15621
|
+
function matchAlias(raw, aliases) {
|
|
15622
|
+
if (typeof raw !== "string") return void 0;
|
|
15623
|
+
const text2 = raw.trim().toLowerCase();
|
|
15624
|
+
const exact = aliases[text2];
|
|
15625
|
+
if (exact) return exact;
|
|
15626
|
+
const words = new Set(text2.split(/[^a-z]+/).filter(Boolean));
|
|
15627
|
+
for (const [alias, value] of Object.entries(aliases)) {
|
|
15628
|
+
if (words.has(alias)) return value;
|
|
15629
|
+
}
|
|
15630
|
+
return void 0;
|
|
15631
|
+
}
|
|
15632
|
+
__name(matchAlias, "matchAlias");
|
|
15633
|
+
function normalizeEndUser(value) {
|
|
15634
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
15635
|
+
const v = value;
|
|
15636
|
+
const age = matchAlias(v.ageBand, AGE_BAND_ALIASES);
|
|
15637
|
+
const gender = matchAlias(v.gender, GENDER_ALIASES);
|
|
15638
|
+
if (!age || !gender || typeof v.usedByBuyer !== "boolean") return void 0;
|
|
15639
|
+
return {
|
|
15640
|
+
ageBand: age,
|
|
15641
|
+
gender,
|
|
15642
|
+
usedByBuyer: v.usedByBuyer
|
|
15643
|
+
};
|
|
15644
|
+
}
|
|
15645
|
+
__name(normalizeEndUser, "normalizeEndUser");
|
|
15646
|
+
var ProductTruthEndUserSchema = z70.object({
|
|
15647
|
+
ageBand: z70.enum(PRODUCT_TRUTH_END_USER_AGE_BANDS),
|
|
15648
|
+
gender: z70.enum(PRODUCT_TRUTH_END_USER_GENDERS),
|
|
15649
|
+
usedByBuyer: z70.boolean()
|
|
15650
|
+
});
|
|
15651
|
+
var MATERIAL_HINTS = ProductMorphology.shape.materialHints.unwrap().element.options;
|
|
15652
|
+
var DEMO_TYPES = FunctionalDemonstration.shape.demoType;
|
|
15653
|
+
var PartSchema = z70.object({
|
|
15654
|
+
name: text(PRODUCT_TRUTH_CAPS.partNameChars),
|
|
15655
|
+
kind: z70.enum([
|
|
15656
|
+
"rigid",
|
|
15657
|
+
"flexible",
|
|
15658
|
+
"joint"
|
|
15659
|
+
]).catch("rigid"),
|
|
15660
|
+
count: z70.number().int().min(1).max(20).catch(1)
|
|
15661
|
+
});
|
|
15662
|
+
var CriticalFeatureSchema = z70.object({
|
|
15663
|
+
name: text(60),
|
|
15664
|
+
importance: text(120),
|
|
15665
|
+
antiPattern: optionalText(80),
|
|
15666
|
+
expectedCount: z70.number().int().min(1).max(20).optional().catch(void 0)
|
|
15667
|
+
});
|
|
15668
|
+
var ConnectionSchema = z70.object({
|
|
15669
|
+
part: text(PRODUCT_TRUTH_CAPS.connectionChars),
|
|
15670
|
+
attachesTo: text(PRODUCT_TRUTH_CAPS.connectionChars),
|
|
15671
|
+
where: optionalText(PRODUCT_TRUTH_CAPS.connectionChars)
|
|
15672
|
+
});
|
|
15673
|
+
var StateSchema = z70.object({
|
|
15674
|
+
key: stateKey,
|
|
15675
|
+
label: optionalText(PRODUCT_TRUTH_CAPS.stateLabelChars),
|
|
15676
|
+
partsPresent: textList(PRODUCT_TRUTH_CAPS.partsPresent, PRODUCT_TRUTH_CAPS.partNameChars),
|
|
15677
|
+
orientationInUse: text(PRODUCT_TRUTH_CAPS.orientationInUseChars),
|
|
15678
|
+
naturalOrientation: ProductNaturalOrientation.optional().catch(void 0)
|
|
15679
|
+
});
|
|
15680
|
+
var StepSchema = z70.object({
|
|
15681
|
+
name: text(PRODUCT_TRUTH_CAPS.stepNameChars),
|
|
15682
|
+
instruction: text(PRODUCT_TRUTH_CAPS.stepInstructionChars),
|
|
15683
|
+
kind: z70.enum([
|
|
15684
|
+
"setup",
|
|
15685
|
+
"use",
|
|
15686
|
+
"finish"
|
|
15687
|
+
]).catch("use")
|
|
15688
|
+
});
|
|
15689
|
+
var AlternateSchema = z70.object({
|
|
15690
|
+
summary: text(PRODUCT_TRUTH_CAPS.summaryChars),
|
|
15691
|
+
stateKey: optionalStateKey,
|
|
15692
|
+
advertisedBySeller: z70.boolean().catch(false)
|
|
15693
|
+
});
|
|
15694
|
+
var DemoSchema = z70.object({
|
|
15695
|
+
demoType: DEMO_TYPES.nullable().catch(null),
|
|
15696
|
+
mustShowFeatures: textList(PRODUCT_TRUTH_CAPS.mustShowFeatures, PRODUCT_TRUTH_CAPS.mustShowFeatureChars),
|
|
15697
|
+
importance: z70.enum([
|
|
15698
|
+
"critical",
|
|
15699
|
+
"important",
|
|
15700
|
+
"nice_to_have"
|
|
15701
|
+
]).catch("important")
|
|
15702
|
+
});
|
|
15703
|
+
var objectOrEmpty = /* @__PURE__ */ __name((v) => v && typeof v === "object" && !Array.isArray(v) ? v : {}, "objectOrEmpty");
|
|
15704
|
+
var ProductTruthBody = z70.object({
|
|
15705
|
+
identity: z70.object({
|
|
15706
|
+
whatItIs: text(PRODUCT_TRUTH_CAPS.whatItIs),
|
|
15707
|
+
deliveryForm: optionalText(PRODUCT_TRUTH_CAPS.deliveryForm),
|
|
15708
|
+
distinctiveMarks: textList(PRODUCT_TRUTH_CAPS.distinctiveMarks, PRODUCT_TRUTH_CAPS.distinctiveMarkChars)
|
|
15709
|
+
}),
|
|
15710
|
+
morphology: z70.preprocess(objectOrEmpty, z70.object({
|
|
15711
|
+
shape: ProductGeometryShape.optional().catch(void 0),
|
|
15712
|
+
crossSection: ProductCrossSection.optional().catch(void 0),
|
|
15713
|
+
cornerProfile: ProductCornerProfile.optional().catch(void 0),
|
|
15714
|
+
silhouetteWidthToHeightRatio: z70.number().min(0.05).max(20).optional().catch(void 0),
|
|
15715
|
+
parts: lenientList(PartSchema, PRODUCT_TRUTH_CAPS.parts),
|
|
15716
|
+
criticalFeatures: lenientList(CriticalFeatureSchema, PRODUCT_TRUTH_CAPS.criticalFeatures),
|
|
15717
|
+
shapeLockLevel: z70.enum([
|
|
15718
|
+
"strict",
|
|
15719
|
+
"moderate",
|
|
15720
|
+
"relaxed"
|
|
15721
|
+
]).optional().catch(void 0),
|
|
15722
|
+
materialHints: z70.preprocess((v) => Array.isArray(v) ? [
|
|
15723
|
+
...new Set(v.filter((m) => MATERIAL_HINTS.includes(m)))
|
|
15724
|
+
].slice(0, 3) : [], z70.array(z70.enum(MATERIAL_HINTS)).max(3))
|
|
15725
|
+
})),
|
|
15726
|
+
assembly: z70.object({
|
|
15727
|
+
connections: lenientList(ConnectionSchema, PRODUCT_TRUTH_CAPS.connections),
|
|
15728
|
+
states: z70.preprocess((v) => {
|
|
15729
|
+
if (!Array.isArray(v)) return v;
|
|
15730
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15731
|
+
return v.filter((entry) => {
|
|
15732
|
+
const parsed = StateSchema.safeParse(entry);
|
|
15733
|
+
if (!parsed.success || seen.has(parsed.data.key)) return false;
|
|
15734
|
+
seen.add(parsed.data.key);
|
|
15735
|
+
return true;
|
|
15736
|
+
}).slice(0, PRODUCT_TRUTH_CAPS.states);
|
|
15737
|
+
}, z70.array(StateSchema).min(1).max(PRODUCT_TRUTH_CAPS.states)),
|
|
15738
|
+
defaultStateKey: optionalStateKey
|
|
15739
|
+
}),
|
|
15740
|
+
use: z70.object({
|
|
15741
|
+
endUser: z70.preprocess(normalizeEndUser, ProductTruthEndUserSchema.optional()).catch(void 0),
|
|
15742
|
+
appliedTo: optionalText(PRODUCT_TRUTH_CAPS.appliedToChars),
|
|
15743
|
+
environment: optionalText(PRODUCT_TRUTH_CAPS.environmentChars),
|
|
15744
|
+
primary: z70.object({
|
|
15745
|
+
summary: text(PRODUCT_TRUTH_CAPS.summaryChars),
|
|
15746
|
+
scene: text(PRODUCT_TRUTH_CAPS.sceneChars),
|
|
15747
|
+
postureAndGrip: optionalText(PRODUCT_TRUTH_CAPS.postureAndGripChars),
|
|
15748
|
+
contactPoint: optionalText(PRODUCT_TRUTH_CAPS.contactPointChars),
|
|
15749
|
+
stateKey: optionalStateKey,
|
|
15750
|
+
steps: z70.preprocess((v) => Array.isArray(v) ? v.filter((s) => StepSchema.safeParse(s).success).slice(0, PRODUCT_TRUTH_CAPS.steps) : v, z70.array(StepSchema).min(1).max(PRODUCT_TRUTH_CAPS.steps)),
|
|
15751
|
+
visibleEffect: optionalText(PRODUCT_TRUTH_CAPS.visibleEffectChars),
|
|
15752
|
+
beforeState: optionalText(PRODUCT_TRUTH_CAPS.beforeStateChars),
|
|
15753
|
+
requiredProps: textList(PRODUCT_TRUTH_CAPS.requiredProps, PRODUCT_TRUTH_CAPS.requiredPropChars)
|
|
15754
|
+
}),
|
|
15755
|
+
alternates: lenientList(AlternateSchema, PRODUCT_TRUTH_CAPS.alternates),
|
|
15756
|
+
misuse: textList(PRODUCT_TRUTH_CAPS.misuse, PRODUCT_TRUTH_CAPS.misuseChars),
|
|
15757
|
+
demo: z70.preprocess(objectOrEmpty, DemoSchema),
|
|
15758
|
+
safety: textList(PRODUCT_TRUTH_CAPS.safety, PRODUCT_TRUTH_CAPS.safetyChars)
|
|
15759
|
+
}),
|
|
15760
|
+
claims: z70.preprocess(objectOrEmpty, z70.object({
|
|
15761
|
+
forbidden: textList(PRODUCT_TRUTH_CAPS.forbiddenClaims, PRODUCT_TRUTH_CAPS.claimChars),
|
|
15762
|
+
unsupported: textList(PRODUCT_TRUTH_CAPS.unsupportedClaims, PRODUCT_TRUTH_CAPS.claimChars)
|
|
15763
|
+
})),
|
|
15764
|
+
visualSummaryEn: optionalText(PRODUCT_TRUTH_CAPS.visualSummaryEnChars),
|
|
15765
|
+
evidence: z70.preprocess(objectOrEmpty, z70.object({
|
|
15766
|
+
photoIndexForState: z70.preprocess((v) => {
|
|
15767
|
+
const out = {};
|
|
15768
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return out;
|
|
15769
|
+
for (const [rawKey, rawIndexes] of Object.entries(v)) {
|
|
15770
|
+
const key = toProductTruthStateKey(rawKey);
|
|
15771
|
+
if (!key || !Array.isArray(rawIndexes)) continue;
|
|
15772
|
+
out[key] = rawIndexes.filter((i) => typeof i === "number" && Number.isInteger(i) && i >= 0 && i < 20);
|
|
15773
|
+
}
|
|
15774
|
+
return out;
|
|
15775
|
+
}, z70.record(z70.array(z70.number().int().min(0).max(19))))
|
|
15776
|
+
})),
|
|
15777
|
+
confidence: z70.preprocess((v) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : 0, z70.number().min(0).max(1))
|
|
15778
|
+
});
|
|
15779
|
+
function resolveStateRefs(body) {
|
|
15780
|
+
const keys = new Set(body.assembly.states.map((s) => s.key));
|
|
15781
|
+
const firstKey = body.assembly.states[0]?.key ?? "";
|
|
15782
|
+
const defaultStateKey = body.assembly.defaultStateKey && keys.has(body.assembly.defaultStateKey) ? body.assembly.defaultStateKey : firstKey;
|
|
15783
|
+
const primaryKey = body.use.primary.stateKey && keys.has(body.use.primary.stateKey) ? body.use.primary.stateKey : defaultStateKey;
|
|
15784
|
+
const photoIndexForState = Object.fromEntries(Object.entries(body.evidence.photoIndexForState).filter(([key]) => keys.has(key)));
|
|
15785
|
+
return {
|
|
15786
|
+
...body,
|
|
15787
|
+
assembly: {
|
|
15788
|
+
...body.assembly,
|
|
15789
|
+
defaultStateKey
|
|
15790
|
+
},
|
|
15791
|
+
use: {
|
|
15792
|
+
...body.use,
|
|
15793
|
+
primary: {
|
|
15794
|
+
...body.use.primary,
|
|
15795
|
+
stateKey: primaryKey
|
|
15796
|
+
},
|
|
15797
|
+
alternates: body.use.alternates.map((alt) => ({
|
|
15798
|
+
...alt,
|
|
15799
|
+
stateKey: alt.stateKey && keys.has(alt.stateKey) ? alt.stateKey : defaultStateKey
|
|
15800
|
+
}))
|
|
15801
|
+
},
|
|
15802
|
+
evidence: {
|
|
15803
|
+
...body.evidence,
|
|
15804
|
+
photoIndexForState
|
|
15805
|
+
}
|
|
15806
|
+
};
|
|
15807
|
+
}
|
|
15808
|
+
__name(resolveStateRefs, "resolveStateRefs");
|
|
15809
|
+
var ProductTruthModelResponseSchema = ProductTruthBody.transform(resolveStateRefs);
|
|
15810
|
+
var provenance = {
|
|
15811
|
+
schemaVersion: z70.literal(PRODUCT_TRUTH_SCHEMA_VERSION),
|
|
15812
|
+
modelId: z70.string().min(1),
|
|
15813
|
+
derivedAt: z70.string().min(1),
|
|
15814
|
+
/** Hash of (image keys + seller text + features): a reader that sees a different current
|
|
15815
|
+
* fingerprint treats the truth as absent. See `computeProductTruthFingerprint` in packages/ai. */
|
|
15816
|
+
sourceFingerprint: z70.string().min(1),
|
|
15817
|
+
/** The seller upload KEYS (deduped, sorted) the fingerprint was computed over. Stored so a
|
|
15818
|
+
* reader can recompute the CURRENT fingerprint from today's seller text + features without
|
|
15819
|
+
* knowing the extract job's inputs (plan Global Constraint "Invalidez"). */
|
|
15820
|
+
sourceImageKeys: z70.array(z70.string().min(1)).max(20).optional()
|
|
15821
|
+
};
|
|
15822
|
+
var ProductTruthKnownSchema = ProductTruthBody.extend({
|
|
15823
|
+
...provenance,
|
|
15824
|
+
known: z70.literal(true)
|
|
15825
|
+
}).transform(resolveStateRefs);
|
|
15826
|
+
var ProductTruthUnknownSchema = z70.object({
|
|
15827
|
+
schemaVersion: z70.literal(PRODUCT_TRUTH_SCHEMA_VERSION),
|
|
15828
|
+
known: z70.literal(false),
|
|
15829
|
+
unknownReason: z70.enum(PRODUCT_TRUTH_UNKNOWN_REASONS),
|
|
15830
|
+
modelId: z70.string().optional(),
|
|
15831
|
+
derivedAt: z70.string().min(1),
|
|
15832
|
+
sourceFingerprint: z70.string().min(1),
|
|
15833
|
+
sourceImageKeys: z70.array(z70.string().min(1)).max(20).optional()
|
|
15834
|
+
});
|
|
15835
|
+
var ProductTruthSchema = z70.union([
|
|
15836
|
+
ProductTruthKnownSchema,
|
|
15837
|
+
ProductTruthUnknownSchema
|
|
15838
|
+
]);
|
|
15839
|
+
|
|
15840
|
+
// ../../packages/types/src/landing-director.ts
|
|
15841
|
+
import { z as z71 } from "zod";
|
|
15842
|
+
var LANDING_DIRECTOR_SCHEMA_VERSION = 1;
|
|
15843
|
+
var LANDING_DIRECTOR_ARM = "categories";
|
|
15844
|
+
var LANDING_DIRECTOR_CAPS = {
|
|
15845
|
+
productIdentity: 200,
|
|
15846
|
+
parts: 8,
|
|
15847
|
+
partName: 40,
|
|
15848
|
+
promise: 200,
|
|
15849
|
+
endUserWho: 80,
|
|
15850
|
+
endUserAppliedTo: 80,
|
|
15851
|
+
forbiddenClaims: 10,
|
|
15852
|
+
claimChars: 80,
|
|
15853
|
+
misuse: 5,
|
|
15854
|
+
misuseChars: 120,
|
|
15855
|
+
assembledStates: 3,
|
|
15856
|
+
statePartsMax: 6,
|
|
15857
|
+
orientation: 160,
|
|
15858
|
+
handling: 140,
|
|
15859
|
+
primaryScene: 200,
|
|
15860
|
+
postureAndGrip: 140,
|
|
15861
|
+
contactPoint: 100,
|
|
15862
|
+
alternates: 3,
|
|
15863
|
+
alternateChars: 120,
|
|
15864
|
+
ambiguities: 4,
|
|
15865
|
+
ambiguityChars: 160,
|
|
15866
|
+
sectionKeyChars: 40,
|
|
15867
|
+
personWho: 80,
|
|
15868
|
+
personPosture: 120,
|
|
15869
|
+
personGrip: 120,
|
|
15870
|
+
scene: 450,
|
|
15871
|
+
panels: 4,
|
|
15872
|
+
panelScene: 200,
|
|
15873
|
+
panelCaption: 60,
|
|
15874
|
+
/** Rendered text: an item over its cap is DROPPED, never cut. The headline cap is 80, not
|
|
15875
|
+
* the design's 70: a dropped headline costs the whole director headline, and 7-10 Spanish
|
|
15876
|
+
* words routinely run 70-80 chars. */
|
|
15877
|
+
headline: 80,
|
|
15878
|
+
subheadline: 90,
|
|
15879
|
+
labels: 3,
|
|
15880
|
+
labelChars: 40,
|
|
15881
|
+
captions: 3,
|
|
15882
|
+
captionChars: 60,
|
|
15883
|
+
props: 4,
|
|
15884
|
+
propChars: 40,
|
|
15885
|
+
mustNotShow: 5,
|
|
15886
|
+
mustNotShowChars: 80
|
|
15887
|
+
};
|
|
15888
|
+
var STATE_KEY_PATTERN = /^[a-z][a-z_]{1,23}$/;
|
|
15889
|
+
function toLandingDirectorStateKey(value) {
|
|
15890
|
+
if (typeof value !== "string") return void 0;
|
|
15891
|
+
const slug = value.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 24).replace(/_+$/g, "");
|
|
15892
|
+
return STATE_KEY_PATTERN.test(slug) ? slug : void 0;
|
|
15893
|
+
}
|
|
15894
|
+
__name(toLandingDirectorStateKey, "toLandingDirectorStateKey");
|
|
15895
|
+
var stateKey2 = z71.preprocess(toLandingDirectorStateKey, z71.string().regex(STATE_KEY_PATTERN));
|
|
15896
|
+
function renderedOptionalText(max) {
|
|
15897
|
+
return z71.preprocess((v) => {
|
|
15898
|
+
if (typeof v !== "string") return void 0;
|
|
15899
|
+
const trimmed = v.replace(/\s+/g, " ").trim();
|
|
15900
|
+
return trimmed.length > 0 && trimmed.length <= max ? trimmed : void 0;
|
|
15901
|
+
}, z71.string().min(1).max(max).optional());
|
|
15902
|
+
}
|
|
15903
|
+
__name(renderedOptionalText, "renderedOptionalText");
|
|
15904
|
+
function renderedTextList(maxItems, maxChars) {
|
|
15905
|
+
return z71.preprocess((v) => {
|
|
15906
|
+
if (!Array.isArray(v)) return [];
|
|
15907
|
+
return v.map((item) => typeof item === "string" ? item.replace(/\s+/g, " ").trim() : "").filter((item) => item.length > 0 && item.length <= maxChars).slice(0, maxItems);
|
|
15908
|
+
}, z71.array(z71.string().min(1).max(maxChars)).max(maxItems));
|
|
15909
|
+
}
|
|
15910
|
+
__name(renderedTextList, "renderedTextList");
|
|
15911
|
+
var C = LANDING_DIRECTOR_CAPS;
|
|
15912
|
+
var PartSchema2 = z71.object({
|
|
15913
|
+
name: lenientText(C.partName),
|
|
15914
|
+
critical: z71.boolean().catch(false)
|
|
15915
|
+
});
|
|
15916
|
+
var AssembledStateSchema = z71.object({
|
|
15917
|
+
key: stateKey2,
|
|
15918
|
+
parts: lenientTextList(C.statePartsMax, C.partName),
|
|
15919
|
+
orientation: lenientText(C.orientation),
|
|
15920
|
+
handling: lenientOptionalText(C.handling)
|
|
15921
|
+
});
|
|
15922
|
+
var END_USER_GENDERS = [
|
|
15923
|
+
"female",
|
|
15924
|
+
"male",
|
|
15925
|
+
"any"
|
|
15926
|
+
];
|
|
15927
|
+
var END_USER_AGE_BANDS = [
|
|
15928
|
+
"child",
|
|
15929
|
+
"teen",
|
|
15930
|
+
"adult",
|
|
15931
|
+
"senior",
|
|
15932
|
+
"any"
|
|
15933
|
+
];
|
|
15934
|
+
var EndUserSchema = z71.object({
|
|
15935
|
+
who: lenientText(C.endUserWho),
|
|
15936
|
+
appliedTo: lenientOptionalText(C.endUserAppliedTo),
|
|
15937
|
+
gender: z71.enum(END_USER_GENDERS).catch("any"),
|
|
15938
|
+
ageBand: z71.enum(END_USER_AGE_BANDS).catch("any")
|
|
15939
|
+
});
|
|
15940
|
+
var LandingDirectorGlobalSchema = z71.object({
|
|
15941
|
+
productIdentity: lenientText(C.productIdentity),
|
|
15942
|
+
parts: lenientList(PartSchema2, C.parts),
|
|
15943
|
+
promise: lenientText(C.promise),
|
|
15944
|
+
endUser: EndUserSchema,
|
|
15945
|
+
forbiddenClaims: lenientTextList(C.forbiddenClaims, C.claimChars),
|
|
15946
|
+
misuse: lenientTextList(C.misuse, C.misuseChars),
|
|
15947
|
+
assembledStates: z71.preprocess((v) => {
|
|
15948
|
+
if (!Array.isArray(v)) return v;
|
|
15949
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15950
|
+
return v.filter((entry) => {
|
|
15951
|
+
const parsed = AssembledStateSchema.safeParse(entry);
|
|
15952
|
+
if (!parsed.success || seen.has(parsed.data.key)) return false;
|
|
15953
|
+
seen.add(parsed.data.key);
|
|
15954
|
+
return true;
|
|
15955
|
+
}).slice(0, C.assembledStates);
|
|
15956
|
+
}, z71.array(AssembledStateSchema).min(1).max(C.assembledStates)),
|
|
15957
|
+
primaryUse: z71.object({
|
|
15958
|
+
assembledState: stateKey2,
|
|
15959
|
+
scene: lenientText(C.primaryScene),
|
|
15960
|
+
postureAndGrip: lenientText(C.postureAndGrip),
|
|
15961
|
+
contactPoint: lenientOptionalText(C.contactPoint)
|
|
15962
|
+
}),
|
|
15963
|
+
primaryRequired: z71.boolean().catch(false),
|
|
15964
|
+
validAlternateUses: lenientTextList(C.alternates, C.alternateChars),
|
|
15965
|
+
ambiguities: lenientTextList(C.ambiguities, C.ambiguityChars)
|
|
15966
|
+
});
|
|
15967
|
+
var LANDING_DIRECTOR_BODY_PARTS = [
|
|
15968
|
+
"hands",
|
|
15969
|
+
"face",
|
|
15970
|
+
"torso",
|
|
15971
|
+
"legs",
|
|
15972
|
+
"feet",
|
|
15973
|
+
"full_body"
|
|
15974
|
+
];
|
|
15975
|
+
var PersonSchema = z71.object({
|
|
15976
|
+
who: lenientText(C.personWho),
|
|
15977
|
+
posture: lenientText(C.personPosture),
|
|
15978
|
+
grip: lenientOptionalText(C.personGrip),
|
|
15979
|
+
bodyPartsVisible: z71.preprocess((v) => Array.isArray(v) ? [
|
|
15980
|
+
...new Set(v.filter((part) => LANDING_DIRECTOR_BODY_PARTS.includes(part)))
|
|
15981
|
+
] : [], z71.array(z71.enum(LANDING_DIRECTOR_BODY_PARTS)))
|
|
15982
|
+
});
|
|
15983
|
+
var LandingDirectorTextSchema = z71.object({
|
|
15984
|
+
headline: renderedOptionalText(C.headline),
|
|
15985
|
+
subheadline: renderedOptionalText(C.subheadline),
|
|
15986
|
+
labels: renderedTextList(C.labels, C.labelChars),
|
|
15987
|
+
captions: renderedTextList(C.captions, C.captionChars)
|
|
15988
|
+
});
|
|
15989
|
+
var PanelSchema = z71.object({
|
|
15990
|
+
scene: lenientText(C.panelScene),
|
|
15991
|
+
caption: renderedOptionalText(C.panelCaption)
|
|
15992
|
+
});
|
|
15993
|
+
var PRODUCT_IN_FRAME_VALUES = [
|
|
15994
|
+
"none",
|
|
15995
|
+
"packshot",
|
|
15996
|
+
"in_use"
|
|
15997
|
+
];
|
|
15998
|
+
var LandingDirectorSectionSchema = z71.object({
|
|
15999
|
+
sectionKey: z71.string().trim().min(1).max(C.sectionKeyChars),
|
|
16000
|
+
/** Present only in the «director» arm. Kept as a flag so the «categorías» validation can
|
|
16001
|
+
* invalidate the section (design §2.3) instead of silently dropping the model's choice. */
|
|
16002
|
+
hasComposition: z71.boolean().default(false),
|
|
16003
|
+
productInFrame: z71.enum(PRODUCT_IN_FRAME_VALUES),
|
|
16004
|
+
assembledState: z71.preprocess((v) => v === null || v === void 0 ? null : toLandingDirectorStateKey(v), z71.string().regex(STATE_KEY_PATTERN).nullable()),
|
|
16005
|
+
usesPrimary: z71.boolean().catch(false),
|
|
16006
|
+
person: z71.preprocess((v) => v === void 0 ? null : v, PersonSchema.nullable()),
|
|
16007
|
+
scene: lenientText(C.scene),
|
|
16008
|
+
panels: lenientList(PanelSchema, C.panels),
|
|
16009
|
+
textToRender: z71.preprocess((v) => v && typeof v === "object" && !Array.isArray(v) ? v : {}, LandingDirectorTextSchema),
|
|
16010
|
+
props: lenientTextList(C.props, C.propChars),
|
|
16011
|
+
mustNotShow: lenientTextList(C.mustNotShow, C.mustNotShowChars)
|
|
16012
|
+
});
|
|
16013
|
+
var LandingDirectorOutputSchema = z71.object({
|
|
16014
|
+
version: z71.literal(LANDING_DIRECTOR_SCHEMA_VERSION),
|
|
16015
|
+
confidence: z71.number().min(0).max(1),
|
|
16016
|
+
global: LandingDirectorGlobalSchema,
|
|
16017
|
+
sections: z71.array(LandingDirectorSectionSchema)
|
|
16018
|
+
});
|
|
16019
|
+
var StoredLandingDirectorSchema = z71.object({
|
|
16020
|
+
version: z71.literal(LANDING_DIRECTOR_SCHEMA_VERSION),
|
|
16021
|
+
arm: z71.literal(LANDING_DIRECTOR_ARM),
|
|
16022
|
+
modelId: z71.string().min(1),
|
|
16023
|
+
generatedAt: z71.string().min(1),
|
|
16024
|
+
confidence: z71.number().min(0).max(1),
|
|
16025
|
+
/** The plan the answer was validated against (sectionKey, or the role on classic). */
|
|
16026
|
+
planSectionKeys: z71.array(z71.string().min(1)),
|
|
16027
|
+
output: LandingDirectorOutputSchema,
|
|
16028
|
+
/** Sections that fell back to the category composition, and why. */
|
|
16029
|
+
fallbackSections: z71.array(z71.object({
|
|
16030
|
+
sectionKey: z71.string().min(1),
|
|
16031
|
+
reason: z71.string().min(1)
|
|
16032
|
+
}))
|
|
16033
|
+
});
|
|
16034
|
+
|
|
16035
|
+
// ../../packages/types/src/flight-recorder.ts
|
|
16036
|
+
import { z as z72 } from "zod";
|
|
16037
|
+
var FlightRecorderGenerationTypeSchema = z72.enum([
|
|
15185
16038
|
"initial",
|
|
15186
16039
|
"regeneration",
|
|
15187
16040
|
"edit",
|
|
15188
16041
|
"retry"
|
|
15189
16042
|
]);
|
|
15190
|
-
var FlightRecorderProviderSchema =
|
|
16043
|
+
var FlightRecorderProviderSchema = z72.enum([
|
|
15191
16044
|
"kie",
|
|
15192
16045
|
"fal"
|
|
15193
16046
|
]);
|
|
15194
|
-
var FlightRecorderElementSchema =
|
|
15195
|
-
name:
|
|
15196
|
-
description:
|
|
15197
|
-
frontalImageUrl:
|
|
15198
|
-
referenceImageUrls:
|
|
15199
|
-
videoUrl:
|
|
15200
|
-
});
|
|
15201
|
-
var FlightRecorderDistillationSchema =
|
|
15202
|
-
actorGender:
|
|
16047
|
+
var FlightRecorderElementSchema = z72.object({
|
|
16048
|
+
name: z72.string().optional(),
|
|
16049
|
+
description: z72.string().optional(),
|
|
16050
|
+
frontalImageUrl: z72.string().optional(),
|
|
16051
|
+
referenceImageUrls: z72.array(z72.string()).optional(),
|
|
16052
|
+
videoUrl: z72.string().optional()
|
|
16053
|
+
});
|
|
16054
|
+
var FlightRecorderDistillationSchema = z72.object({
|
|
16055
|
+
actorGender: z72.enum([
|
|
15203
16056
|
"female",
|
|
15204
16057
|
"male",
|
|
15205
16058
|
"mixed"
|
|
15206
|
-
]).or(
|
|
15207
|
-
voiceId:
|
|
15208
|
-
cameraMotion:
|
|
15209
|
-
}).catchall(
|
|
15210
|
-
var FlightRecorderEntrySchema =
|
|
15211
|
-
schemaVersion:
|
|
15212
|
-
id:
|
|
15213
|
-
taskId:
|
|
15214
|
-
runId:
|
|
15215
|
-
sequence:
|
|
15216
|
-
timestamp:
|
|
15217
|
-
provider: FlightRecorderProviderSchema.or(
|
|
15218
|
-
model:
|
|
15219
|
-
prompt:
|
|
15220
|
-
durationSeconds:
|
|
15221
|
-
aspectRatio:
|
|
15222
|
-
resolution:
|
|
15223
|
-
startImageUrl:
|
|
15224
|
-
endImageUrl:
|
|
15225
|
-
generateAudio:
|
|
16059
|
+
]).or(z72.string()).optional(),
|
|
16060
|
+
voiceId: z72.string().optional(),
|
|
16061
|
+
cameraMotion: z72.string().optional()
|
|
16062
|
+
}).catchall(z72.unknown());
|
|
16063
|
+
var FlightRecorderEntrySchema = z72.object({
|
|
16064
|
+
schemaVersion: z72.literal(1).optional(),
|
|
16065
|
+
id: z72.string().min(1),
|
|
16066
|
+
taskId: z72.string().min(1),
|
|
16067
|
+
runId: z72.string().min(1),
|
|
16068
|
+
sequence: z72.number().int().nonnegative(),
|
|
16069
|
+
timestamp: z72.string().min(1),
|
|
16070
|
+
provider: FlightRecorderProviderSchema.or(z72.string()),
|
|
16071
|
+
model: z72.string().min(1),
|
|
16072
|
+
prompt: z72.string(),
|
|
16073
|
+
durationSeconds: z72.number().nonnegative(),
|
|
16074
|
+
aspectRatio: z72.string().optional(),
|
|
16075
|
+
resolution: z72.string().optional(),
|
|
16076
|
+
startImageUrl: z72.string().optional(),
|
|
16077
|
+
endImageUrl: z72.string().optional(),
|
|
16078
|
+
generateAudio: z72.boolean().optional(),
|
|
15226
16079
|
// Correlation & pipeline metadata
|
|
15227
|
-
angleKey:
|
|
15228
|
-
sceneIndex:
|
|
16080
|
+
angleKey: z72.string().optional(),
|
|
16081
|
+
sceneIndex: z72.number().int().nonnegative().optional(),
|
|
15229
16082
|
generationType: FlightRecorderGenerationTypeSchema,
|
|
15230
|
-
operationId:
|
|
15231
|
-
revisionId:
|
|
15232
|
-
attemptNumber:
|
|
16083
|
+
operationId: z72.string().min(1).optional(),
|
|
16084
|
+
revisionId: z72.string().min(1).optional(),
|
|
16085
|
+
attemptNumber: z72.number().int().nonnegative().default(1),
|
|
15233
16086
|
// Elements & Distillation
|
|
15234
|
-
elements:
|
|
16087
|
+
elements: z72.array(FlightRecorderElementSchema).optional(),
|
|
15235
16088
|
distillation: FlightRecorderDistillationSchema.optional(),
|
|
15236
16089
|
// Raw payload (sanitized of secrets)
|
|
15237
|
-
rawPayload:
|
|
16090
|
+
rawPayload: z72.record(z72.unknown())
|
|
15238
16091
|
});
|
|
15239
|
-
var FlightRecorderQuerySchema =
|
|
15240
|
-
taskId:
|
|
15241
|
-
runId:
|
|
15242
|
-
sceneIndex:
|
|
15243
|
-
angleKey:
|
|
16092
|
+
var FlightRecorderQuerySchema = z72.object({
|
|
16093
|
+
taskId: z72.string().min(1),
|
|
16094
|
+
runId: z72.string().optional(),
|
|
16095
|
+
sceneIndex: z72.number().int().nonnegative().optional(),
|
|
16096
|
+
angleKey: z72.string().optional(),
|
|
15244
16097
|
generationType: FlightRecorderGenerationTypeSchema.optional(),
|
|
15245
|
-
cursor:
|
|
15246
|
-
limit:
|
|
16098
|
+
cursor: z72.number().int().nonnegative().optional(),
|
|
16099
|
+
limit: z72.number().int().positive().optional()
|
|
15247
16100
|
});
|
|
15248
|
-
var FlightRecorderResponseSchema =
|
|
15249
|
-
taskId:
|
|
15250
|
-
runId:
|
|
15251
|
-
entries:
|
|
16101
|
+
var FlightRecorderResponseSchema = z72.object({
|
|
16102
|
+
taskId: z72.string().min(1),
|
|
16103
|
+
runId: z72.string().min(1),
|
|
16104
|
+
entries: z72.array(FlightRecorderEntrySchema),
|
|
15252
16105
|
lastCall: FlightRecorderEntrySchema.nullable(),
|
|
15253
|
-
count:
|
|
15254
|
-
nextCursor:
|
|
16106
|
+
count: z72.number().int().nonnegative(),
|
|
16107
|
+
nextCursor: z72.number().int().nonnegative().nullable()
|
|
15255
16108
|
});
|
|
15256
16109
|
|
|
15257
16110
|
// src/http.ts
|
|
@@ -15354,6 +16207,9 @@ function buildProxyUrl(opts) {
|
|
|
15354
16207
|
}
|
|
15355
16208
|
__name(buildProxyUrl, "buildProxyUrl");
|
|
15356
16209
|
|
|
16210
|
+
// src/http.ts
|
|
16211
|
+
import { getOAuthProtectedResourceMetadataUrl as getOAuthProtectedResourceMetadataUrl2 } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
16212
|
+
|
|
15357
16213
|
// src/auth/http-oauth.ts
|
|
15358
16214
|
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
|
|
15359
16215
|
import { getOAuthProtectedResourceMetadataUrl, mcpAuthMetadataRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
@@ -15426,8 +16282,8 @@ async function readResponseText(response, maxBytes) {
|
|
|
15426
16282
|
}
|
|
15427
16283
|
__name(readResponseText, "readResponseText");
|
|
15428
16284
|
async function readResponseJson(response, maxBytes) {
|
|
15429
|
-
const
|
|
15430
|
-
return JSON.parse(
|
|
16285
|
+
const text2 = await readResponseText(response, maxBytes);
|
|
16286
|
+
return JSON.parse(text2);
|
|
15431
16287
|
}
|
|
15432
16288
|
__name(readResponseJson, "readResponseJson");
|
|
15433
16289
|
|
|
@@ -15760,22 +16616,23 @@ import { InsufficientScopeError, InvalidTokenError } from "@modelcontextprotocol
|
|
|
15760
16616
|
import { createRemoteJWKSet as createRemoteJWKSet2, jwtVerify as jwtVerify2 } from "jose";
|
|
15761
16617
|
|
|
15762
16618
|
// src/auth/oauth-config.ts
|
|
15763
|
-
import { z as
|
|
16619
|
+
import { z as z73 } from "zod";
|
|
15764
16620
|
var MCP_RESOURCE = new URL("https://mcp.productmaker.app/mcp");
|
|
15765
16621
|
var MCP_SCOPE = "mcp:access";
|
|
16622
|
+
var REFRESH_SCOPE = "offline_access";
|
|
15766
16623
|
var STATIC_CLIENT_ISSUER = new URL(MCP_RESOURCE.origin);
|
|
15767
|
-
var oauthEnvSchema =
|
|
15768
|
-
MCP_REMOTE_AUTH_MODE:
|
|
15769
|
-
MCP_AUTH0_ISSUER:
|
|
15770
|
-
MCP_AUTH0_AUDIENCE:
|
|
16624
|
+
var oauthEnvSchema = z73.object({
|
|
16625
|
+
MCP_REMOTE_AUTH_MODE: z73.literal("oauth"),
|
|
16626
|
+
MCP_AUTH0_ISSUER: z73.string().trim().min(1),
|
|
16627
|
+
MCP_AUTH0_AUDIENCE: z73.string().trim().refine((value) => value === MCP_RESOURCE.href, {
|
|
15771
16628
|
message: `must equal ${MCP_RESOURCE.href}`
|
|
15772
16629
|
}),
|
|
15773
|
-
MCP_AUTH0_CLIENT_ID:
|
|
15774
|
-
MCP_AUTH0_CLIENT_SECRET:
|
|
15775
|
-
PM_API_AUDIENCE:
|
|
15776
|
-
AUTH0_AUDIENCE:
|
|
15777
|
-
MCP_STATIC_CLIENT_ID:
|
|
15778
|
-
MCP_STATIC_CLIENT_SECRET:
|
|
16630
|
+
MCP_AUTH0_CLIENT_ID: z73.string().trim().min(1),
|
|
16631
|
+
MCP_AUTH0_CLIENT_SECRET: z73.string().trim().min(1),
|
|
16632
|
+
PM_API_AUDIENCE: z73.string().trim().min(1),
|
|
16633
|
+
AUTH0_AUDIENCE: z73.string().trim().min(1),
|
|
16634
|
+
MCP_STATIC_CLIENT_ID: z73.string().trim().min(1).optional(),
|
|
16635
|
+
MCP_STATIC_CLIENT_SECRET: z73.string().trim().min(1).optional()
|
|
15779
16636
|
});
|
|
15780
16637
|
function formatZodIssues(error) {
|
|
15781
16638
|
return error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ");
|
|
@@ -16059,7 +16916,7 @@ function badRequest(res, description) {
|
|
|
16059
16916
|
__name(badRequest, "badRequest");
|
|
16060
16917
|
function createOAuthProxyRouter(options) {
|
|
16061
16918
|
const router = express.Router();
|
|
16062
|
-
const
|
|
16919
|
+
const stateKey3 = deriveStateKey(options.staticClient.clientSecret);
|
|
16063
16920
|
const now = options.now ?? (() => Date.now());
|
|
16064
16921
|
router.get(OAUTH_PROXY_PATHS.authorize, (req, res) => {
|
|
16065
16922
|
const redirectUri = firstQueryValue(req.query.redirect_uri);
|
|
@@ -16088,7 +16945,7 @@ function createOAuthProxyRouter(options) {
|
|
|
16088
16945
|
}
|
|
16089
16946
|
}
|
|
16090
16947
|
upstream.searchParams.set("redirect_uri", PROXY_CALLBACK_URL);
|
|
16091
|
-
upstream.searchParams.set("state", sealState(
|
|
16948
|
+
upstream.searchParams.set("state", sealState(stateKey3, {
|
|
16092
16949
|
redirectUri,
|
|
16093
16950
|
...firstQueryValue(req.query.state) === void 0 ? {} : {
|
|
16094
16951
|
clientState: firstQueryValue(req.query.state)
|
|
@@ -16098,7 +16955,7 @@ function createOAuthProxyRouter(options) {
|
|
|
16098
16955
|
});
|
|
16099
16956
|
router.get(OAUTH_PROXY_PATHS.callback, (req, res) => {
|
|
16100
16957
|
const sealed = firstQueryValue(req.query.state);
|
|
16101
|
-
const opened = sealed === void 0 ? void 0 : openState(
|
|
16958
|
+
const opened = sealed === void 0 ? void 0 : openState(stateKey3, sealed, now());
|
|
16102
16959
|
if (!opened) {
|
|
16103
16960
|
badRequest(res, "state is missing, tampered with, or expired");
|
|
16104
16961
|
return;
|
|
@@ -16146,13 +17003,10 @@ function createOAuthProxyRouter(options) {
|
|
|
16146
17003
|
});
|
|
16147
17004
|
return;
|
|
16148
17005
|
}
|
|
17006
|
+
body.set("client_secret", options.staticClient.clientSecret);
|
|
16149
17007
|
const headers = {
|
|
16150
17008
|
"content-type": "application/x-www-form-urlencoded"
|
|
16151
17009
|
};
|
|
16152
|
-
const authorization = req.headers.authorization;
|
|
16153
|
-
if (typeof authorization === "string") {
|
|
16154
|
-
headers.authorization = authorization;
|
|
16155
|
-
}
|
|
16156
17010
|
let upstream;
|
|
16157
17011
|
let payload;
|
|
16158
17012
|
try {
|
|
@@ -16185,11 +17039,12 @@ var REGISTRATION_RATE_LIMIT = {
|
|
|
16185
17039
|
windowMs: 6e4,
|
|
16186
17040
|
max: 120
|
|
16187
17041
|
};
|
|
17042
|
+
var PUBLIC_CLIENT_SECRET_PLACEHOLDER = "pm-public-client";
|
|
16188
17043
|
function buildStaticClientInformation(staticClient, requested) {
|
|
16189
17044
|
return {
|
|
16190
17045
|
client_id: staticClient.clientId,
|
|
16191
|
-
client_secret:
|
|
16192
|
-
// 0 = never expires (RFC 7591 §3.2.1); the
|
|
17046
|
+
client_secret: PUBLIC_CLIENT_SECRET_PLACEHOLDER,
|
|
17047
|
+
// 0 = never expires (RFC 7591 §3.2.1); the placeholder is a constant.
|
|
16193
17048
|
client_secret_expires_at: 0,
|
|
16194
17049
|
token_endpoint_auth_method: "client_secret_post",
|
|
16195
17050
|
redirect_uris: requested.redirect_uris,
|
|
@@ -16200,7 +17055,12 @@ function buildStaticClientInformation(staticClient, requested) {
|
|
|
16200
17055
|
response_types: [
|
|
16201
17056
|
"code"
|
|
16202
17057
|
],
|
|
16203
|
-
|
|
17058
|
+
// Clients that defer scope choice to the server get the full intended
|
|
17059
|
+
// set: `mcp:access` for the resource plus `offline_access` so the session
|
|
17060
|
+
// can refresh instead of dying an interactive re-auth every 900s access
|
|
17061
|
+
// token (the auth loop seen in production). A client that names its own
|
|
17062
|
+
// scope list is echoed verbatim — its choice wins.
|
|
17063
|
+
scope: requested.scope ?? `${MCP_SCOPE} ${REFRESH_SCOPE}`,
|
|
16204
17064
|
...requested.client_name !== void 0 ? {
|
|
16205
17065
|
client_name: requested.client_name
|
|
16206
17066
|
} : {}
|
|
@@ -16247,8 +17107,12 @@ function buildStaticClientAuthorizationServerMetadata(auth0Metadata) {
|
|
|
16247
17107
|
token_endpoint_auth_methods_supported: [
|
|
16248
17108
|
"client_secret_post"
|
|
16249
17109
|
],
|
|
17110
|
+
// `offline_access` is what lets a client ASK for a refresh token; without
|
|
17111
|
+
// it in scopes_supported, conforming clients never request it and every
|
|
17112
|
+
// 900s access-token expiry becomes an interactive re-auth loop.
|
|
16250
17113
|
scopes_supported: [
|
|
16251
|
-
MCP_SCOPE
|
|
17114
|
+
MCP_SCOPE,
|
|
17115
|
+
REFRESH_SCOPE
|
|
16252
17116
|
],
|
|
16253
17117
|
service_documentation: "https://productmaker.app/"
|
|
16254
17118
|
};
|
|
@@ -16760,7 +17624,10 @@ var ApiClient = class {
|
|
|
16760
17624
|
method: "POST",
|
|
16761
17625
|
headers: {
|
|
16762
17626
|
...authHeaders,
|
|
16763
|
-
"Content-Type": "application/json"
|
|
17627
|
+
"Content-Type": "application/json",
|
|
17628
|
+
...options?.idempotencyKey ? {
|
|
17629
|
+
"Idempotency-Key": options.idempotencyKey
|
|
17630
|
+
} : {}
|
|
16764
17631
|
},
|
|
16765
17632
|
body: JSON.stringify(body)
|
|
16766
17633
|
}, options);
|
|
@@ -16882,7 +17749,7 @@ var ApiClient = class {
|
|
|
16882
17749
|
};
|
|
16883
17750
|
|
|
16884
17751
|
// src/index.ts
|
|
16885
|
-
var MCP_VERSION = "8.
|
|
17752
|
+
var MCP_VERSION = "8.3.0";
|
|
16886
17753
|
|
|
16887
17754
|
// src/meta-upload-app.ts
|
|
16888
17755
|
import { readFileSync } from "fs";
|
|
@@ -16954,7 +17821,7 @@ function registerMediaUploadAppResource(server, resolveEndpoint) {
|
|
|
16954
17821
|
__name(registerMediaUploadAppResource, "registerMediaUploadAppResource");
|
|
16955
17822
|
|
|
16956
17823
|
// src/tools/tasks.ts
|
|
16957
|
-
import { z as
|
|
17824
|
+
import { z as z77 } from "zod";
|
|
16958
17825
|
|
|
16959
17826
|
// src/resolve-image.ts
|
|
16960
17827
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -17515,10 +18382,10 @@ async function resolveMediaRef(ref, opts) {
|
|
|
17515
18382
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
17516
18383
|
|
|
17517
18384
|
// src/tools/shared.ts
|
|
17518
|
-
import { z as
|
|
17519
|
-
var Lang =
|
|
17520
|
-
var Country =
|
|
17521
|
-
var LandingStrategyEnum =
|
|
18385
|
+
import { z as z74 } from "zod";
|
|
18386
|
+
var Lang = z74.string().regex(/^[a-z]{2}(?:[-_][a-zA-Z]{2})?$/, "Usa un c\xF3digo de idioma BCP-47, por ejemplo es, en-US o pt-BR").max(10);
|
|
18387
|
+
var Country = z74.string().min(2).max(3);
|
|
18388
|
+
var LandingStrategyEnum = z74.enum([
|
|
17522
18389
|
"auto",
|
|
17523
18390
|
"classic",
|
|
17524
18391
|
"pain_solution",
|
|
@@ -17527,12 +18394,12 @@ var LandingStrategyEnum = z70.enum([
|
|
|
17527
18394
|
"offer_value",
|
|
17528
18395
|
"lifestyle_identity"
|
|
17529
18396
|
]);
|
|
17530
|
-
var LandingImageFormatEnum =
|
|
18397
|
+
var LandingImageFormatEnum = z74.enum([
|
|
17531
18398
|
"9:16",
|
|
17532
18399
|
"4:5",
|
|
17533
18400
|
"1:1"
|
|
17534
18401
|
]);
|
|
17535
|
-
var VideoPresetEnum =
|
|
18402
|
+
var VideoPresetEnum = z74.enum([
|
|
17536
18403
|
"auto",
|
|
17537
18404
|
"ugc_dynamic",
|
|
17538
18405
|
"ugc_how_to",
|
|
@@ -17545,21 +18412,21 @@ var VideoPresetEnum = z70.enum([
|
|
|
17545
18412
|
"pixar_problem",
|
|
17546
18413
|
"pixar_sassy"
|
|
17547
18414
|
]);
|
|
17548
|
-
var VideoStructureEnum =
|
|
18415
|
+
var VideoStructureEnum = z74.enum([
|
|
17549
18416
|
"single_take",
|
|
17550
18417
|
"multi_scene"
|
|
17551
18418
|
]);
|
|
17552
|
-
var VideoQualityEnum =
|
|
18419
|
+
var VideoQualityEnum = z74.enum([
|
|
17553
18420
|
"economy",
|
|
17554
18421
|
"medium",
|
|
17555
18422
|
"pro"
|
|
17556
18423
|
]);
|
|
17557
|
-
var VideoRouteIdEnum =
|
|
18424
|
+
var VideoRouteIdEnum = z74.enum([
|
|
17558
18425
|
"single_take",
|
|
17559
18426
|
"multi_scene",
|
|
17560
18427
|
"free"
|
|
17561
18428
|
]);
|
|
17562
|
-
var TaskCreativeVariantVideoPresetEnum =
|
|
18429
|
+
var TaskCreativeVariantVideoPresetEnum = z74.enum([
|
|
17563
18430
|
"auto",
|
|
17564
18431
|
"ugc_dynamic",
|
|
17565
18432
|
"ugc_how_to",
|
|
@@ -17576,53 +18443,43 @@ var VIDEO_PRESET_RESOLUTION = {
|
|
|
17576
18443
|
auto: null,
|
|
17577
18444
|
ugc_dynamic: {
|
|
17578
18445
|
videoStyle: "ai_dynamic",
|
|
17579
|
-
narrativeStyle: "review"
|
|
17580
|
-
videoModel: "veo"
|
|
18446
|
+
narrativeStyle: "review"
|
|
17581
18447
|
},
|
|
17582
18448
|
ugc_how_to: {
|
|
17583
18449
|
videoStyle: "ai_dynamic",
|
|
17584
|
-
narrativeStyle: "ugc_how_to"
|
|
17585
|
-
videoModel: "veo"
|
|
18450
|
+
narrativeStyle: "ugc_how_to"
|
|
17586
18451
|
},
|
|
17587
18452
|
ugc_unboxing_chaos: {
|
|
17588
18453
|
videoStyle: "ai_dynamic",
|
|
17589
|
-
narrativeStyle: "ugc_unboxing_chaos"
|
|
17590
|
-
videoModel: "veo"
|
|
18454
|
+
narrativeStyle: "ugc_unboxing_chaos"
|
|
17591
18455
|
},
|
|
17592
18456
|
problem_solution: {
|
|
17593
18457
|
videoStyle: "ai_dynamic",
|
|
17594
|
-
narrativeStyle: "problem_product_cta"
|
|
17595
|
-
videoModel: "kling"
|
|
18458
|
+
narrativeStyle: "problem_product_cta"
|
|
17596
18459
|
},
|
|
17597
18460
|
pixar_problem: {
|
|
17598
18461
|
videoStyle: "ai_dynamic",
|
|
17599
|
-
narrativeStyle: "animated_problem_pixar"
|
|
17600
|
-
videoModel: "kling"
|
|
18462
|
+
narrativeStyle: "animated_problem_pixar"
|
|
17601
18463
|
},
|
|
17602
18464
|
pixar_sassy: {
|
|
17603
18465
|
videoStyle: "ai_dynamic",
|
|
17604
|
-
narrativeStyle: "sassy_product_pixar"
|
|
17605
|
-
videoModel: "seedance"
|
|
18466
|
+
narrativeStyle: "sassy_product_pixar"
|
|
17606
18467
|
},
|
|
17607
18468
|
product_showcase: {
|
|
17608
18469
|
videoStyle: "product_only",
|
|
17609
|
-
narrativeStyle: "product_showcase"
|
|
17610
|
-
videoModel: "veo"
|
|
18470
|
+
narrativeStyle: "product_showcase"
|
|
17611
18471
|
},
|
|
17612
18472
|
wearable_showcase: {
|
|
17613
18473
|
videoStyle: "ugc",
|
|
17614
|
-
narrativeStyle: "wearable_showcase"
|
|
17615
|
-
videoModel: "veo"
|
|
18474
|
+
narrativeStyle: "wearable_showcase"
|
|
17616
18475
|
},
|
|
17617
18476
|
cinematic: {
|
|
17618
18477
|
videoStyle: "product_only",
|
|
17619
|
-
narrativeStyle: "cinematic"
|
|
17620
|
-
videoModel: "grok"
|
|
18478
|
+
narrativeStyle: "cinematic"
|
|
17621
18479
|
},
|
|
17622
18480
|
wild_card: {
|
|
17623
18481
|
videoStyle: "product_only",
|
|
17624
|
-
narrativeStyle: "wild_card"
|
|
17625
|
-
videoModel: "grok"
|
|
18482
|
+
narrativeStyle: "wild_card"
|
|
17626
18483
|
}
|
|
17627
18484
|
};
|
|
17628
18485
|
function resolveVideoPreset(preset) {
|
|
@@ -17630,7 +18487,7 @@ function resolveVideoPreset(preset) {
|
|
|
17630
18487
|
return VIDEO_PRESET_RESOLUTION[preset] ?? null;
|
|
17631
18488
|
}
|
|
17632
18489
|
__name(resolveVideoPreset, "resolveVideoPreset");
|
|
17633
|
-
var VideoTierEnum =
|
|
18490
|
+
var VideoTierEnum = z74.enum([
|
|
17634
18491
|
"basic"
|
|
17635
18492
|
]);
|
|
17636
18493
|
function resolveVideoTier(_tier) {
|
|
@@ -17670,22 +18527,22 @@ function assertSemanticVideoDurationCompatibility(structure, quality, durationSe
|
|
|
17670
18527
|
}
|
|
17671
18528
|
}
|
|
17672
18529
|
__name(assertSemanticVideoDurationCompatibility, "assertSemanticVideoDurationCompatibility");
|
|
17673
|
-
var VideoResolutionEnum =
|
|
18530
|
+
var VideoResolutionEnum = z74.enum([
|
|
17674
18531
|
"480p",
|
|
17675
18532
|
"720p"
|
|
17676
18533
|
]);
|
|
17677
|
-
var CreativeGenderEnum =
|
|
18534
|
+
var CreativeGenderEnum = z74.enum([
|
|
17678
18535
|
"female",
|
|
17679
18536
|
"male",
|
|
17680
18537
|
"mixed"
|
|
17681
18538
|
]);
|
|
17682
|
-
var VideoStyleEnum =
|
|
18539
|
+
var VideoStyleEnum = z74.enum([
|
|
17683
18540
|
"ugc",
|
|
17684
18541
|
"cartoon_3d",
|
|
17685
18542
|
"product_only",
|
|
17686
18543
|
"ai_dynamic"
|
|
17687
18544
|
]);
|
|
17688
|
-
var NarrativeStyleEnum =
|
|
18545
|
+
var NarrativeStyleEnum = z74.enum([
|
|
17689
18546
|
"review",
|
|
17690
18547
|
"ugc_how_to",
|
|
17691
18548
|
"problem_product_cta",
|
|
@@ -17703,7 +18560,7 @@ var NarrativeStyleEnum = z70.enum([
|
|
|
17703
18560
|
"ugc_demo_review",
|
|
17704
18561
|
"market"
|
|
17705
18562
|
]);
|
|
17706
|
-
var PublicNarrativeStyleEnum =
|
|
18563
|
+
var PublicNarrativeStyleEnum = z74.enum([
|
|
17707
18564
|
"review",
|
|
17708
18565
|
"ugc_how_to",
|
|
17709
18566
|
"ugc_unboxing_chaos",
|
|
@@ -17716,7 +18573,7 @@ var PublicNarrativeStyleEnum = z70.enum([
|
|
|
17716
18573
|
"sassy_product_pixar",
|
|
17717
18574
|
"market"
|
|
17718
18575
|
]);
|
|
17719
|
-
var
|
|
18576
|
+
var TaskVideoModelEnum = z74.enum([
|
|
17720
18577
|
"veo",
|
|
17721
18578
|
"wan",
|
|
17722
18579
|
"bytedance",
|
|
@@ -17724,29 +18581,29 @@ var VideoModelEnum = z70.enum([
|
|
|
17724
18581
|
"grok",
|
|
17725
18582
|
"kling"
|
|
17726
18583
|
]);
|
|
17727
|
-
var ProductReferenceUserChoiceEnum =
|
|
18584
|
+
var ProductReferenceUserChoiceEnum = z74.enum([
|
|
17728
18585
|
"same_product",
|
|
17729
18586
|
"variants",
|
|
17730
18587
|
"different_products",
|
|
17731
18588
|
"combo_distinct"
|
|
17732
18589
|
]);
|
|
17733
|
-
var ProductReferenceCandidateGroupingEnum =
|
|
18590
|
+
var ProductReferenceCandidateGroupingEnum = z74.enum([
|
|
17734
18591
|
"same_product_same_variant",
|
|
17735
18592
|
"same_product_different_variants",
|
|
17736
18593
|
"different_products",
|
|
17737
18594
|
"unclear"
|
|
17738
18595
|
]);
|
|
17739
|
-
var ProductReferenceScopeEnum =
|
|
18596
|
+
var ProductReferenceScopeEnum = z74.enum([
|
|
17740
18597
|
"base",
|
|
17741
18598
|
"video_view_pack"
|
|
17742
18599
|
]);
|
|
17743
|
-
var ProductReferenceResolutionDecisionEnum =
|
|
18600
|
+
var ProductReferenceResolutionDecisionEnum = z74.enum([
|
|
17744
18601
|
"approve",
|
|
17745
18602
|
"reject",
|
|
17746
18603
|
"observed_only",
|
|
17747
18604
|
"correct_view"
|
|
17748
18605
|
]);
|
|
17749
|
-
var ProductReferenceRejectionReasonEnum =
|
|
18606
|
+
var ProductReferenceRejectionReasonEnum = z74.enum([
|
|
17750
18607
|
"replacement_requested",
|
|
17751
18608
|
"wrong_product_identity",
|
|
17752
18609
|
"wrong_variant",
|
|
@@ -17757,13 +18614,13 @@ var ProductReferenceRejectionReasonEnum = z70.enum([
|
|
|
17757
18614
|
"not_a_product_photo",
|
|
17758
18615
|
"other"
|
|
17759
18616
|
]);
|
|
17760
|
-
var GapDurationChoiceMirror =
|
|
17761
|
-
angleIndex:
|
|
17762
|
-
recommendedSec:
|
|
17763
|
-
|
|
17764
|
-
|
|
18617
|
+
var GapDurationChoiceMirror = z74.object({
|
|
18618
|
+
angleIndex: z74.number().int().nonnegative(),
|
|
18619
|
+
recommendedSec: z74.union([
|
|
18620
|
+
z74.literal(15),
|
|
18621
|
+
z74.literal(30)
|
|
17765
18622
|
]),
|
|
17766
|
-
estimatedDeltaCredits:
|
|
18623
|
+
estimatedDeltaCredits: z74.number().int().nonnegative().optional()
|
|
17767
18624
|
});
|
|
17768
18625
|
function buildSelectedImageUrlsHash(urls) {
|
|
17769
18626
|
const input = [
|
|
@@ -17773,13 +18630,13 @@ function buildSelectedImageUrlsHash(urls) {
|
|
|
17773
18630
|
return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
|
|
17774
18631
|
}
|
|
17775
18632
|
__name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
|
|
17776
|
-
var ImageCreativeStyleEnum =
|
|
18633
|
+
var ImageCreativeStyleEnum = z74.enum([
|
|
17777
18634
|
"studio",
|
|
17778
18635
|
"floating",
|
|
17779
18636
|
"ingredients",
|
|
17780
18637
|
"in_use"
|
|
17781
18638
|
]);
|
|
17782
|
-
var ImageCreativeArchetypeEnum =
|
|
18639
|
+
var ImageCreativeArchetypeEnum = z74.enum([
|
|
17783
18640
|
"direct_response_product_ad",
|
|
17784
18641
|
"hero_outcome",
|
|
17785
18642
|
"offer_value_prop",
|
|
@@ -17788,20 +18645,20 @@ var ImageCreativeArchetypeEnum = z70.enum([
|
|
|
17788
18645
|
"problem_solution",
|
|
17789
18646
|
"lifestyle_identity"
|
|
17790
18647
|
]);
|
|
17791
|
-
var ImageCreativeVisualTreatmentEnum =
|
|
18648
|
+
var ImageCreativeVisualTreatmentEnum = z74.enum([
|
|
17792
18649
|
"performance_product_ad",
|
|
17793
18650
|
"premium_dtc_carousel"
|
|
17794
18651
|
]);
|
|
17795
|
-
var ImageCreativeAspectRatioEnum =
|
|
18652
|
+
var ImageCreativeAspectRatioEnum = z74.enum([
|
|
17796
18653
|
"1:1",
|
|
17797
18654
|
"4:5",
|
|
17798
18655
|
"9:16",
|
|
17799
18656
|
"16:9"
|
|
17800
18657
|
]);
|
|
17801
|
-
var ImageRefSchema =
|
|
17802
|
-
url:
|
|
17803
|
-
path:
|
|
17804
|
-
assetId:
|
|
18658
|
+
var ImageRefSchema = z74.object({
|
|
18659
|
+
url: z74.string().url().optional().describe('URL p\xFAblica HTTPS de la foto. Ej: una URL de Shopify CDN, Cloudinary, Imgur. \xDAsala cuando el usuario te diga "est\xE1 en X.com/foto.jpg" o cuando la foto ya est\xE9 hospedada.'),
|
|
18660
|
+
path: z74.string().min(1).optional().describe('Ruta del archivo en el computador del usuario. Ej: "/Users/maria/Desktop/foto.jpg", "~/Pictures/camiseta.png", "C:\\Users\\Maria\\Desktop\\foto.jpg". El MCP local la lee del disco y la sube. SOLO funciona cuando el MCP corre local (stdio). NO funciona en mcp.productmaker.app (HTTP) \u2014 ah\xED usa `url`.'),
|
|
18661
|
+
assetId: z74.string().uuid().optional().describe("ID opaco devuelto por check_media_upload. \xDAsalo para una imagen subida desde el MCP App; ProductMaker adopta el archivo directamente y el MCP nunca descarga sus bytes.")
|
|
17805
18662
|
}).refine((v) => [
|
|
17806
18663
|
v.url,
|
|
17807
18664
|
v.path,
|
|
@@ -17809,11 +18666,11 @@ var ImageRefSchema = z70.object({
|
|
|
17809
18666
|
].filter(Boolean).length === 1, {
|
|
17810
18667
|
message: "each image entry must have exactly one of `url`, `path`, or `assetId`"
|
|
17811
18668
|
});
|
|
17812
|
-
var ImagesInput =
|
|
18669
|
+
var ImagesInput = z74.array(ImageRefSchema).min(1).max(5).superRefine((images, ctx) => {
|
|
17813
18670
|
const assetCount = images.filter((image) => Boolean(image.assetId)).length;
|
|
17814
18671
|
if (assetCount > 0 && assetCount !== images.length) {
|
|
17815
18672
|
ctx.addIssue({
|
|
17816
|
-
code:
|
|
18673
|
+
code: z74.ZodIssueCode.custom,
|
|
17817
18674
|
message: "all image entries must use assetId, or all must use URL/path sources"
|
|
17818
18675
|
});
|
|
17819
18676
|
}
|
|
@@ -17876,19 +18733,24 @@ async function submitCreative(api, opts) {
|
|
|
17876
18733
|
const taskId = r.taskId ?? r.jobId;
|
|
17877
18734
|
return asText({
|
|
17878
18735
|
taskId,
|
|
17879
|
-
|
|
17880
|
-
|
|
18736
|
+
// Replay-aware: a keyed retry may land on a task that is already
|
|
18737
|
+
// processing/done — report the real status, not a hopeful "queued".
|
|
18738
|
+
status: r.status ?? "queued",
|
|
18739
|
+
estimatedSeconds: opts.estimatedSeconds,
|
|
18740
|
+
...r.replayed === true ? {
|
|
18741
|
+
replayed: true
|
|
18742
|
+
} : {}
|
|
17881
18743
|
});
|
|
17882
18744
|
}
|
|
17883
18745
|
__name(submitCreative, "submitCreative");
|
|
17884
|
-
var ResearchTargetCountryEnum =
|
|
18746
|
+
var ResearchTargetCountryEnum = z74.enum([
|
|
17885
18747
|
"CO",
|
|
17886
18748
|
"MX",
|
|
17887
18749
|
"PE",
|
|
17888
18750
|
"EC",
|
|
17889
18751
|
"CL"
|
|
17890
18752
|
]);
|
|
17891
|
-
var ResearchCategoryEnum =
|
|
18753
|
+
var ResearchCategoryEnum = z74.enum([
|
|
17892
18754
|
"auto",
|
|
17893
18755
|
"automotive",
|
|
17894
18756
|
"kitchen",
|
|
@@ -17957,7 +18819,7 @@ var OVERWRITES_EXTERNAL = {
|
|
|
17957
18819
|
idempotentHint: false,
|
|
17958
18820
|
openWorldHint: false
|
|
17959
18821
|
};
|
|
17960
|
-
var DailyControlStatusEnum =
|
|
18822
|
+
var DailyControlStatusEnum = z74.enum([
|
|
17961
18823
|
"ok",
|
|
17962
18824
|
"sales_unavailable",
|
|
17963
18825
|
"timezone_unavailable",
|
|
@@ -17970,19 +18832,19 @@ var DailyControlStatusEnum = z70.enum([
|
|
|
17970
18832
|
"product_missing",
|
|
17971
18833
|
"store_unreachable"
|
|
17972
18834
|
]);
|
|
17973
|
-
var DailyControlLightEnum =
|
|
18835
|
+
var DailyControlLightEnum = z74.enum([
|
|
17974
18836
|
"green",
|
|
17975
18837
|
"amber",
|
|
17976
18838
|
"red",
|
|
17977
18839
|
"none"
|
|
17978
18840
|
]);
|
|
17979
|
-
var DailyControlVerdictEnum =
|
|
18841
|
+
var DailyControlVerdictEnum = z74.enum([
|
|
17980
18842
|
"scale",
|
|
17981
18843
|
"hold",
|
|
17982
18844
|
"kill",
|
|
17983
18845
|
"insufficient_sample"
|
|
17984
18846
|
]);
|
|
17985
|
-
var DailyControlSourceEnum =
|
|
18847
|
+
var DailyControlSourceEnum = z74.enum([
|
|
17986
18848
|
"shopify",
|
|
17987
18849
|
"shopify_settling",
|
|
17988
18850
|
"user",
|
|
@@ -19070,29 +19932,29 @@ ${parsed.data.error ? `${escapeMarkdownText(parsed.data.error)}
|
|
|
19070
19932
|
__name(renderVideoScriptMarkdown, "renderVideoScriptMarkdown");
|
|
19071
19933
|
|
|
19072
19934
|
// src/tools/media-presentation.ts
|
|
19073
|
-
import { z as
|
|
19074
|
-
var HttpsPresentationUrlSchema =
|
|
19935
|
+
import { z as z75 } from "zod";
|
|
19936
|
+
var HttpsPresentationUrlSchema = z75.string().url().max(2048).refine((value) => new URL(value).protocol === "https:");
|
|
19075
19937
|
var ProductMakerPreviewUrlSchema = HttpsPresentationUrlSchema.refine((value) => {
|
|
19076
19938
|
const hostname = new URL(value).hostname.toLowerCase();
|
|
19077
19939
|
return hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
|
|
19078
19940
|
}, "preview URL must be owned by ProductMaker");
|
|
19079
|
-
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) =>
|
|
19080
|
-
var PresentationImageSchema =
|
|
19081
|
-
kind:
|
|
19941
|
+
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) => z75.number().int().positive().max(maximum), "positiveIntAtMost");
|
|
19942
|
+
var PresentationImageSchema = z75.object({
|
|
19943
|
+
kind: z75.literal("image"),
|
|
19082
19944
|
url: HttpsPresentationUrlSchema,
|
|
19083
|
-
mimeType:
|
|
19945
|
+
mimeType: z75.enum([
|
|
19084
19946
|
"image/jpeg",
|
|
19085
19947
|
"image/png"
|
|
19086
19948
|
]),
|
|
19087
19949
|
byteSize: positiveIntAtMost(5 * 1024 * 1024),
|
|
19088
19950
|
width: positiveIntAtMost(16384),
|
|
19089
19951
|
height: positiveIntAtMost(16384),
|
|
19090
|
-
alt:
|
|
19952
|
+
alt: z75.string().trim().min(1).max(240)
|
|
19091
19953
|
}).strict();
|
|
19092
19954
|
var McpMediaPresentationSchema = PresentationImageSchema;
|
|
19093
|
-
var GalleryImageSchema =
|
|
19955
|
+
var GalleryImageSchema = z75.object({
|
|
19094
19956
|
url: ProductMakerPreviewUrlSchema,
|
|
19095
|
-
alt:
|
|
19957
|
+
alt: z75.string().trim().min(1).max(240)
|
|
19096
19958
|
});
|
|
19097
19959
|
var MCP_GALLERY_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
19098
19960
|
async function imageBlocks(image, transport) {
|
|
@@ -19241,11 +20103,175 @@ ${summary}`,
|
|
|
19241
20103
|
}
|
|
19242
20104
|
__name(requirePaidCreativeConfirmation, "requirePaidCreativeConfirmation");
|
|
19243
20105
|
|
|
20106
|
+
// src/tools/stable-uuid.ts
|
|
20107
|
+
import { createHash as createHash3 } from "crypto";
|
|
20108
|
+
function canonicalize(value) {
|
|
20109
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
20110
|
+
if (value === null || typeof value !== "object") return value;
|
|
20111
|
+
const record = value;
|
|
20112
|
+
return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
|
|
20113
|
+
key,
|
|
20114
|
+
canonicalize(record[key])
|
|
20115
|
+
]));
|
|
20116
|
+
}
|
|
20117
|
+
__name(canonicalize, "canonicalize");
|
|
20118
|
+
function canonicalJson(value) {
|
|
20119
|
+
return JSON.stringify(canonicalize(value));
|
|
20120
|
+
}
|
|
20121
|
+
__name(canonicalJson, "canonicalJson");
|
|
20122
|
+
function deriveUuidV5(name, namespace) {
|
|
20123
|
+
const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
|
|
20124
|
+
const digest = createHash3("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
20125
|
+
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
20126
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
20127
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
20128
|
+
const hex = bytes.toString("hex");
|
|
20129
|
+
return [
|
|
20130
|
+
hex.slice(0, 8),
|
|
20131
|
+
hex.slice(8, 12),
|
|
20132
|
+
hex.slice(12, 16),
|
|
20133
|
+
hex.slice(16, 20),
|
|
20134
|
+
hex.slice(20)
|
|
20135
|
+
].join("-");
|
|
20136
|
+
}
|
|
20137
|
+
__name(deriveUuidV5, "deriveUuidV5");
|
|
20138
|
+
|
|
20139
|
+
// src/tools/request-key.ts
|
|
20140
|
+
var PAID_REQUEST_KEY_WINDOW_MS = 15 * 60 * 1e3;
|
|
20141
|
+
var PAID_REQUEST_KEY_NAMESPACE = "8f3d6b1c-2a47-4e5b-9c0d-6e7f8a9b0c1d";
|
|
20142
|
+
var PARAPHRASE_PRONE_FIELDS = /* @__PURE__ */ new Set([
|
|
20143
|
+
"additionalContext",
|
|
20144
|
+
"productContext",
|
|
20145
|
+
"scriptNotes",
|
|
20146
|
+
"creativeDirection",
|
|
20147
|
+
"buyerPersona",
|
|
20148
|
+
"actorDescription",
|
|
20149
|
+
"description",
|
|
20150
|
+
"idempotencyKey",
|
|
20151
|
+
"clientRequestId"
|
|
20152
|
+
]);
|
|
20153
|
+
var URL_FIELDS = /* @__PURE__ */ new Set([
|
|
20154
|
+
"landingUrl",
|
|
20155
|
+
"productUrl",
|
|
20156
|
+
"url",
|
|
20157
|
+
"serviceLandingUrl"
|
|
20158
|
+
]);
|
|
20159
|
+
var TRACKING_PARAM_PATTERN = /^(utm_\w+|fbclid|gclid|msclkid|mc_cid|mc_eid|igshid)$/;
|
|
20160
|
+
var LANGUAGE_FIELDS = /* @__PURE__ */ new Set([
|
|
20161
|
+
"language",
|
|
20162
|
+
"lang"
|
|
20163
|
+
]);
|
|
20164
|
+
var CASE_FOLD_FIELDS = /* @__PURE__ */ new Set([
|
|
20165
|
+
"country",
|
|
20166
|
+
"language",
|
|
20167
|
+
"lang"
|
|
20168
|
+
]);
|
|
20169
|
+
var ORDER_INSIGNIFICANT_ARRAY_FIELDS = /* @__PURE__ */ new Set([
|
|
20170
|
+
"outputs",
|
|
20171
|
+
"selectedOutputs",
|
|
20172
|
+
"features"
|
|
20173
|
+
]);
|
|
20174
|
+
function sortCanonical(items) {
|
|
20175
|
+
return [
|
|
20176
|
+
...items
|
|
20177
|
+
].sort((a, b) => {
|
|
20178
|
+
const ka = canonicalJson(a);
|
|
20179
|
+
const kb = canonicalJson(b);
|
|
20180
|
+
return ka < kb ? -1 : ka > kb ? 1 : 0;
|
|
20181
|
+
});
|
|
20182
|
+
}
|
|
20183
|
+
__name(sortCanonical, "sortCanonical");
|
|
20184
|
+
function isRecord3(value) {
|
|
20185
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20186
|
+
}
|
|
20187
|
+
__name(isRecord3, "isRecord");
|
|
20188
|
+
function collectLanguageCodes(value, out) {
|
|
20189
|
+
if (Array.isArray(value)) {
|
|
20190
|
+
const items = value;
|
|
20191
|
+
for (const item of items) collectLanguageCodes(item, out);
|
|
20192
|
+
return out;
|
|
20193
|
+
}
|
|
20194
|
+
if (!isRecord3(value)) return out;
|
|
20195
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
20196
|
+
if (LANGUAGE_FIELDS.has(key) && typeof entry === "string") {
|
|
20197
|
+
const base = entry.trim().split(/[-_]/)[0]?.toLowerCase();
|
|
20198
|
+
if (base) out.add(base);
|
|
20199
|
+
}
|
|
20200
|
+
collectLanguageCodes(entry, out);
|
|
20201
|
+
}
|
|
20202
|
+
return out;
|
|
20203
|
+
}
|
|
20204
|
+
__name(collectLanguageCodes, "collectLanguageCodes");
|
|
20205
|
+
function normalizeSearchForKey(search) {
|
|
20206
|
+
if (!search) return "";
|
|
20207
|
+
const params = [
|
|
20208
|
+
...new URLSearchParams(search).entries()
|
|
20209
|
+
].filter(([key]) => !TRACKING_PARAM_PATTERN.test(key.toLowerCase())).sort(([ka, va], [kb, vb]) => ka === kb ? va < vb ? -1 : va > vb ? 1 : 0 : ka < kb ? -1 : 1);
|
|
20210
|
+
if (!params.length) return "";
|
|
20211
|
+
const out = new URLSearchParams();
|
|
20212
|
+
for (const [key, value] of params) out.append(key, value);
|
|
20213
|
+
return `?${out.toString()}`;
|
|
20214
|
+
}
|
|
20215
|
+
__name(normalizeSearchForKey, "normalizeSearchForKey");
|
|
20216
|
+
function normalizeUrlForKey(value, languages) {
|
|
20217
|
+
const trimmed = value.trim();
|
|
20218
|
+
let url;
|
|
20219
|
+
try {
|
|
20220
|
+
url = new URL(trimmed);
|
|
20221
|
+
} catch {
|
|
20222
|
+
return trimmed.toLowerCase();
|
|
20223
|
+
}
|
|
20224
|
+
const host = url.hostname.toLowerCase().replace(/^www\./, "");
|
|
20225
|
+
const port = url.port ? `:${url.port}` : "";
|
|
20226
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
20227
|
+
const soleSegment = segments.length === 1 ? segments[0] : void 0;
|
|
20228
|
+
if (soleSegment !== void 0 && languages.has(soleSegment.toLowerCase())) {
|
|
20229
|
+
segments.pop();
|
|
20230
|
+
}
|
|
20231
|
+
const path3 = segments.length ? `/${segments.join("/")}` : "";
|
|
20232
|
+
return host + port + path3 + normalizeSearchForKey(url.search);
|
|
20233
|
+
}
|
|
20234
|
+
__name(normalizeUrlForKey, "normalizeUrlForKey");
|
|
20235
|
+
function normalizeForKey(value, languages) {
|
|
20236
|
+
if (Array.isArray(value)) {
|
|
20237
|
+
const items = value;
|
|
20238
|
+
return items.map((item) => normalizeForKey(item, languages));
|
|
20239
|
+
}
|
|
20240
|
+
if (!isRecord3(value)) return value;
|
|
20241
|
+
const out = {};
|
|
20242
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
20243
|
+
if (PARAPHRASE_PRONE_FIELDS.has(key)) {
|
|
20244
|
+
if (key === "description" && value.mode === "described") {
|
|
20245
|
+
out[key] = normalizeForKey(entry, languages);
|
|
20246
|
+
}
|
|
20247
|
+
continue;
|
|
20248
|
+
}
|
|
20249
|
+
out[key] = URL_FIELDS.has(key) && typeof entry === "string" ? normalizeUrlForKey(entry, languages) : CASE_FOLD_FIELDS.has(key) && typeof entry === "string" ? (
|
|
20250
|
+
// separator too or a host alternating between them splits the key.
|
|
20251
|
+
entry.trim().toLowerCase().replace(/_/g, "-")
|
|
20252
|
+
) : ORDER_INSIGNIFICANT_ARRAY_FIELDS.has(key) && Array.isArray(entry) ? sortCanonical(entry.map((item) => normalizeForKey(item, languages))) : normalizeForKey(entry, languages);
|
|
20253
|
+
}
|
|
20254
|
+
return out;
|
|
20255
|
+
}
|
|
20256
|
+
__name(normalizeForKey, "normalizeForKey");
|
|
20257
|
+
function derivePaidRequestKey(args) {
|
|
20258
|
+
const languages = collectLanguageCodes(args.input, /* @__PURE__ */ new Set());
|
|
20259
|
+
const semanticInput = normalizeForKey(args.input, languages);
|
|
20260
|
+
const bucket = Math.floor(args.nowMs / PAID_REQUEST_KEY_WINDOW_MS);
|
|
20261
|
+
return deriveUuidV5(canonicalJson({
|
|
20262
|
+
user: args.fingerprint,
|
|
20263
|
+
tool: args.tool,
|
|
20264
|
+
input: semanticInput,
|
|
20265
|
+
bucket
|
|
20266
|
+
}), PAID_REQUEST_KEY_NAMESPACE);
|
|
20267
|
+
}
|
|
20268
|
+
__name(derivePaidRequestKey, "derivePaidRequestKey");
|
|
20269
|
+
|
|
19244
20270
|
// src/tools/winning-product-image.ts
|
|
19245
|
-
import { z as
|
|
19246
|
-
var WinnerReferenceInput =
|
|
19247
|
-
runId:
|
|
19248
|
-
candidateSourceId:
|
|
20271
|
+
import { z as z76 } from "zod";
|
|
20272
|
+
var WinnerReferenceInput = z76.object({
|
|
20273
|
+
runId: z76.string().uuid(),
|
|
20274
|
+
candidateSourceId: z76.string().trim().min(1).max(160)
|
|
19249
20275
|
}).strict().describe("Referencia opaca de un producto devuelto por find_winning_products/get_product_research_run.");
|
|
19250
20276
|
async function resolveWinnerImages(api, winnerReference, images) {
|
|
19251
20277
|
if (!winnerReference) return images;
|
|
@@ -19282,59 +20308,59 @@ var createProductTaskSingleOutputDescription = renderCreateProductTaskAvoidWhenF
|
|
|
19282
20308
|
var createServiceVideoGuidance = getProductMakerCapabilityModelGuidance("create_service_video");
|
|
19283
20309
|
var listTasksGuidance = getProductMakerCapabilityModelGuidance("list_tasks");
|
|
19284
20310
|
var editTaskDraftGuidance = getProductMakerCapabilityModelGuidance("edit_task_draft");
|
|
19285
|
-
var CreateProductTaskInput =
|
|
20311
|
+
var CreateProductTaskInput = z77.object({
|
|
19286
20312
|
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
19287
20313
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
19288
|
-
projectId:
|
|
19289
|
-
productUrl:
|
|
20314
|
+
projectId: z77.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
|
|
20315
|
+
productUrl: z77.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."),
|
|
19290
20316
|
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.'),
|
|
19291
20317
|
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.'),
|
|
19292
|
-
price:
|
|
19293
|
-
compareAtPrice:
|
|
19294
|
-
offer:
|
|
19295
|
-
paymentMethods:
|
|
19296
|
-
productContext:
|
|
19297
|
-
productLabel:
|
|
20318
|
+
price: z77.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.'),
|
|
20319
|
+
compareAtPrice: z77.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".'),
|
|
20320
|
+
offer: z77.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.'),
|
|
20321
|
+
paymentMethods: z77.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.'),
|
|
20322
|
+
productContext: z77.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."),
|
|
20323
|
+
productLabel: z77.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.'),
|
|
19298
20324
|
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), excepto "problem_solution", que requiere 30s Multiescena. Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "problem_solution" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (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.'),
|
|
19299
|
-
durationSeconds:
|
|
19300
|
-
|
|
19301
|
-
|
|
20325
|
+
durationSeconds: z77.union([
|
|
20326
|
+
z77.literal(15),
|
|
20327
|
+
z77.literal(30)
|
|
19302
20328
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
19303
|
-
pauseForAngleReview:
|
|
19304
|
-
aiDecidesAll:
|
|
20329
|
+
pauseForAngleReview: z77.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."),
|
|
20330
|
+
aiDecidesAll: z77.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."),
|
|
19305
20331
|
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.'),
|
|
19306
20332
|
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.'),
|
|
19307
20333
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
19308
20334
|
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).'),
|
|
19309
|
-
videoAngleCount:
|
|
20335
|
+
videoAngleCount: z77.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."),
|
|
19310
20336
|
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.'),
|
|
19311
|
-
outputs:
|
|
20337
|
+
outputs: z77.array(z77.enum([
|
|
19312
20338
|
"landing",
|
|
19313
20339
|
"video",
|
|
19314
20340
|
"image"
|
|
19315
20341
|
])).min(2).max(3).optional().describe(`Opcional. Qu\xE9 outputs generar. Default: todos ["landing","video","image"]. Para 2 outputs: ["landing","image"] (sin video), ["video","image"] (sin landing), ["landing","video"] (sin imagen). ${createProductTaskSingleOutputDescription}`),
|
|
19316
|
-
competitorScanEnabled:
|
|
19317
|
-
replicateWinnerWhenFound:
|
|
19318
|
-
brandId:
|
|
19319
|
-
actorId:
|
|
20342
|
+
competitorScanEnabled: z77.boolean().optional().describe("Opcional. Debe enviarse true solo despu\xE9s de que el usuario acepte estudiar la competencia (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. Omitir no autoriza el estudio profundo."),
|
|
20343
|
+
replicateWinnerWhenFound: z77.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."),
|
|
20344
|
+
brandId: z77.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."),
|
|
20345
|
+
actorId: z77.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."),
|
|
19320
20346
|
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.'),
|
|
19321
|
-
creativeDirection:
|
|
19322
|
-
headline:
|
|
19323
|
-
subheadline:
|
|
20347
|
+
creativeDirection: z77.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.'),
|
|
20348
|
+
headline: z77.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."),
|
|
20349
|
+
subheadline: z77.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."),
|
|
19324
20350
|
landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution", "social_proof" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".'),
|
|
19325
20351
|
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".')
|
|
19326
20352
|
}).strict();
|
|
19327
|
-
var CreateServiceVideoInput =
|
|
19328
|
-
landingUrl:
|
|
19329
|
-
additionalContext:
|
|
20353
|
+
var CreateServiceVideoInput = z77.object({
|
|
20354
|
+
landingUrl: z77.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
|
|
20355
|
+
additionalContext: z77.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
|
|
19330
20356
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
19331
|
-
voiceId:
|
|
19332
|
-
brandId:
|
|
19333
|
-
reviewMode:
|
|
20357
|
+
voiceId: z77.string().trim().min(1).optional().describe('Opcional. Voz de ElevenLabs para toda la narraci\xF3n. Usa "auto" para que la IA la alinee con el presentador o env\xEDa un ID concreto de la lista de voces de la web.'),
|
|
20358
|
+
brandId: z77.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
|
|
20359
|
+
reviewMode: z77.enum([
|
|
19334
20360
|
"automatic",
|
|
19335
20361
|
"guided"
|
|
19336
20362
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
19337
|
-
videoPreset:
|
|
20363
|
+
videoPreset: z77.enum([
|
|
19338
20364
|
"auto",
|
|
19339
20365
|
"ugc_dynamic",
|
|
19340
20366
|
"ugc_how_to",
|
|
@@ -19344,128 +20370,128 @@ var CreateServiceVideoInput = z73.object({
|
|
|
19344
20370
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
19345
20371
|
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.')
|
|
19346
20372
|
}).strict();
|
|
19347
|
-
var GetTaskStatusInput =
|
|
19348
|
-
taskId:
|
|
19349
|
-
waitSeconds:
|
|
20373
|
+
var GetTaskStatusInput = z77.object({
|
|
20374
|
+
taskId: z77.string().min(1),
|
|
20375
|
+
waitSeconds: z77.number().int().min(0).max(120).optional()
|
|
19350
20376
|
});
|
|
19351
|
-
var ListTasksInput =
|
|
19352
|
-
limit:
|
|
19353
|
-
offset:
|
|
19354
|
-
search:
|
|
20377
|
+
var ListTasksInput = z77.object({
|
|
20378
|
+
limit: z77.number().int().min(1).max(50).optional(),
|
|
20379
|
+
offset: z77.number().int().min(0).optional(),
|
|
20380
|
+
search: z77.string().optional()
|
|
19355
20381
|
});
|
|
19356
20382
|
var MCP_REVIEW_LIST_FIELD_MAX = 1500;
|
|
19357
20383
|
var MCP_REVIEW_FEATURE_ITEM_MAX = 240;
|
|
19358
|
-
var EditTaskDraftInput =
|
|
19359
|
-
taskId:
|
|
19360
|
-
angleIndex:
|
|
19361
|
-
angle:
|
|
19362
|
-
angle:
|
|
19363
|
-
buyerPersona:
|
|
19364
|
-
actor:
|
|
19365
|
-
actorId:
|
|
19366
|
-
gender:
|
|
20384
|
+
var EditTaskDraftInput = z77.object({
|
|
20385
|
+
taskId: z77.string().min(1),
|
|
20386
|
+
angleIndex: z77.number().int().min(0),
|
|
20387
|
+
angle: z77.object({
|
|
20388
|
+
angle: z77.string().min(1).max(480),
|
|
20389
|
+
buyerPersona: z77.string().min(1).max(360),
|
|
20390
|
+
actor: z77.string().min(1).max(360).optional(),
|
|
20391
|
+
actorId: z77.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."),
|
|
20392
|
+
gender: z77.enum([
|
|
19367
20393
|
"female",
|
|
19368
20394
|
"male"
|
|
19369
20395
|
]).optional(),
|
|
19370
|
-
scriptNotes:
|
|
20396
|
+
scriptNotes: z77.string().max(2e3).optional()
|
|
19371
20397
|
})
|
|
19372
20398
|
});
|
|
19373
|
-
var PendingActionPayload =
|
|
19374
|
-
|
|
19375
|
-
kind:
|
|
20399
|
+
var PendingActionPayload = z77.discriminatedUnion("kind", [
|
|
20400
|
+
z77.object({
|
|
20401
|
+
kind: z77.literal("product_reference_set_approval"),
|
|
19376
20402
|
scope: ProductReferenceScopeEnum.optional(),
|
|
19377
|
-
scopeKey:
|
|
19378
|
-
scopeSnapshotId:
|
|
19379
|
-
expectedScopeHash:
|
|
20403
|
+
scopeKey: z77.string().min(1).max(120).optional(),
|
|
20404
|
+
scopeSnapshotId: z77.string().uuid().optional(),
|
|
20405
|
+
expectedScopeHash: z77.string().min(1).max(128).optional(),
|
|
19380
20406
|
decision: ProductReferenceResolutionDecisionEnum,
|
|
19381
20407
|
reason: ProductReferenceRejectionReasonEnum.optional(),
|
|
19382
|
-
affectedRefKeys:
|
|
19383
|
-
affectedPhotoNumbers:
|
|
19384
|
-
recoveryAction:
|
|
20408
|
+
affectedRefKeys: z77.array(z77.string().min(1).max(200)).min(1).optional(),
|
|
20409
|
+
affectedPhotoNumbers: z77.array(z77.number().int().positive()).min(1).optional(),
|
|
20410
|
+
recoveryAction: z77.enum([
|
|
19385
20411
|
"remove",
|
|
19386
20412
|
"replace",
|
|
19387
20413
|
"upload",
|
|
19388
20414
|
"return_to_selection"
|
|
19389
20415
|
]).optional()
|
|
19390
20416
|
}),
|
|
19391
|
-
|
|
19392
|
-
kind:
|
|
19393
|
-
selectedPhotoNumbers:
|
|
20417
|
+
z77.object({
|
|
20418
|
+
kind: z77.literal("product_reference_confirmation"),
|
|
20419
|
+
selectedPhotoNumbers: z77.array(z77.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."),
|
|
19394
20420
|
userChoice: ProductReferenceUserChoiceEnum.optional(),
|
|
19395
|
-
confirmedProductNames:
|
|
20421
|
+
confirmedProductNames: z77.array(z77.string().min(1).max(120)).min(2).max(8).optional(),
|
|
19396
20422
|
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."),
|
|
19397
20423
|
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."),
|
|
19398
|
-
useMultiSceneShowcase:
|
|
20424
|
+
useMultiSceneShowcase: z77.boolean().optional().describe("Compatibilidad con confirmaciones antiguas: si es true junto con userChoice=variants, conserva el showcase multiescena.")
|
|
19399
20425
|
}),
|
|
19400
|
-
|
|
19401
|
-
kind:
|
|
20426
|
+
z77.object({
|
|
20427
|
+
kind: z77.literal("angle_review_approval"),
|
|
19402
20428
|
// All edits are optional — empty action body just approves the
|
|
19403
20429
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
19404
20430
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
19405
20431
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
19406
|
-
customAngle:
|
|
20432
|
+
customAngle: z77.string().min(1).max(480).optional(),
|
|
19407
20433
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
19408
20434
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
19409
|
-
painPoints:
|
|
19410
|
-
benefits:
|
|
20435
|
+
painPoints: z77.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
20436
|
+
benefits: z77.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
19411
20437
|
// Same policy as the two fields above: the strictest downstream contract
|
|
19412
20438
|
// wins. The image endpoint caps each feature at 240 while landing/video
|
|
19413
20439
|
// accept 300, and this single action routes to whichever the task is — so
|
|
19414
20440
|
// accepting 300 here turns a 250-character feature on an image task into
|
|
19415
20441
|
// an opaque HTTP 400 after the model already believed it had been saved.
|
|
19416
|
-
features:
|
|
19417
|
-
buyerPersona:
|
|
20442
|
+
features: z77.array(z77.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
|
|
20443
|
+
buyerPersona: z77.string().min(1).max(360).optional(),
|
|
19418
20444
|
narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) requiere Multiescena de 30s: en un video de 15s en formato Un solo clip el servidor la rechaza.'),
|
|
19419
|
-
actor:
|
|
19420
|
-
actorId:
|
|
19421
|
-
creativeDirection:
|
|
19422
|
-
selectedAngleNumbers:
|
|
19423
|
-
angleEdits:
|
|
19424
|
-
angleNumber:
|
|
19425
|
-
narrativeSelection:
|
|
20445
|
+
actor: z77.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."),
|
|
20446
|
+
actorId: z77.string().max(120).nullable().optional(),
|
|
20447
|
+
creativeDirection: z77.string().max(2e3).optional(),
|
|
20448
|
+
selectedAngleNumbers: z77.array(z77.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."),
|
|
20449
|
+
angleEdits: z77.array(z77.object({
|
|
20450
|
+
angleNumber: z77.number().int().positive(),
|
|
20451
|
+
narrativeSelection: z77.enum([
|
|
19426
20452
|
"market",
|
|
19427
20453
|
"guided"
|
|
19428
20454
|
]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo."),
|
|
19429
|
-
angle:
|
|
19430
|
-
buyerPersona:
|
|
20455
|
+
angle: z77.string().min(1).max(480).optional(),
|
|
20456
|
+
buyerPersona: z77.string().min(1).max(360).optional(),
|
|
19431
20457
|
narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) requiere Multiescena de 30s: en un video de 15s en formato Un solo clip el servidor la rechaza.'),
|
|
19432
|
-
actor:
|
|
19433
|
-
actorId:
|
|
19434
|
-
creativeDirection:
|
|
20458
|
+
actor: z77.string().min(1).max(360).optional(),
|
|
20459
|
+
actorId: z77.string().max(120).nullable().optional(),
|
|
20460
|
+
creativeDirection: z77.string().max(2e3).optional()
|
|
19435
20461
|
}).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)."),
|
|
19436
|
-
gapDurationSeconds:
|
|
19437
|
-
|
|
19438
|
-
|
|
20462
|
+
gapDurationSeconds: z77.union([
|
|
20463
|
+
z77.literal(15),
|
|
20464
|
+
z77.literal(30)
|
|
19439
20465
|
]).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.")
|
|
19440
20466
|
}),
|
|
19441
|
-
|
|
19442
|
-
kind:
|
|
19443
|
-
angleId:
|
|
19444
|
-
angleNumber:
|
|
19445
|
-
content:
|
|
19446
|
-
creativeDirection:
|
|
19447
|
-
actor:
|
|
20467
|
+
z77.object({
|
|
20468
|
+
kind: z77.literal("service_angle_approval"),
|
|
20469
|
+
angleId: z77.string().uuid().optional(),
|
|
20470
|
+
angleNumber: z77.number().int().positive().optional(),
|
|
20471
|
+
content: z77.string().min(1).max(480).optional(),
|
|
20472
|
+
creativeDirection: z77.string().min(1).max(1500).optional(),
|
|
20473
|
+
actor: z77.string().min(1).max(360).optional()
|
|
19448
20474
|
}),
|
|
19449
|
-
|
|
19450
|
-
kind:
|
|
19451
|
-
voiceovers:
|
|
19452
|
-
visualDirections:
|
|
19453
|
-
voiceoversByScene:
|
|
19454
|
-
visualDirectionsByScene:
|
|
20475
|
+
z77.object({
|
|
20476
|
+
kind: z77.literal("service_script_approval"),
|
|
20477
|
+
voiceovers: z77.record(z77.string(), z77.string().min(1).max(600)).optional(),
|
|
20478
|
+
visualDirections: z77.record(z77.string(), z77.string().min(1).max(1200)).optional(),
|
|
20479
|
+
voiceoversByScene: z77.record(z77.string(), z77.string().min(1).max(600)).optional(),
|
|
20480
|
+
visualDirectionsByScene: z77.record(z77.string(), z77.string().min(1).max(1200)).optional()
|
|
19455
20481
|
})
|
|
19456
20482
|
]);
|
|
19457
|
-
var ResolvePendingActionInput =
|
|
19458
|
-
taskId:
|
|
20483
|
+
var ResolvePendingActionInput = z77.object({
|
|
20484
|
+
taskId: z77.string().min(1),
|
|
19459
20485
|
action: PendingActionPayload
|
|
19460
20486
|
}).strict();
|
|
19461
20487
|
function resolveVariantConfirmVideoTier(action) {
|
|
19462
20488
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
19463
20489
|
}
|
|
19464
20490
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
19465
|
-
var StatusForResolveSchema =
|
|
20491
|
+
var StatusForResolveSchema = z77.object({
|
|
19466
20492
|
// Metadata used to select the backend resume contract. Older API responses
|
|
19467
20493
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
19468
|
-
kind:
|
|
20494
|
+
kind: z77.enum([
|
|
19469
20495
|
"autopilot",
|
|
19470
20496
|
"video",
|
|
19471
20497
|
"image",
|
|
@@ -19473,18 +20499,18 @@ var StatusForResolveSchema = z73.object({
|
|
|
19473
20499
|
"mix",
|
|
19474
20500
|
"service"
|
|
19475
20501
|
]).optional(),
|
|
19476
|
-
selectedOutputs:
|
|
20502
|
+
selectedOutputs: z77.array(z77.enum([
|
|
19477
20503
|
"landing",
|
|
19478
20504
|
"video",
|
|
19479
20505
|
"image"
|
|
19480
20506
|
])).optional(),
|
|
19481
|
-
videoAngleCount:
|
|
19482
|
-
ugcFormat:
|
|
20507
|
+
videoAngleCount: z77.number().int().min(1).max(3).optional(),
|
|
20508
|
+
ugcFormat: z77.enum([
|
|
19483
20509
|
"single_8s",
|
|
19484
20510
|
"single_15s",
|
|
19485
20511
|
"double_30s"
|
|
19486
20512
|
]).optional(),
|
|
19487
|
-
videoStyle:
|
|
20513
|
+
videoStyle: z77.enum([
|
|
19488
20514
|
"ugc",
|
|
19489
20515
|
"cartoon_3d",
|
|
19490
20516
|
"product_only",
|
|
@@ -19493,79 +20519,72 @@ var StatusForResolveSchema = z73.object({
|
|
|
19493
20519
|
videoStructure: VideoStructureEnum.optional(),
|
|
19494
20520
|
videoQuality: VideoQualityEnum.optional(),
|
|
19495
20521
|
videoRouteId: VideoRouteIdEnum.optional(),
|
|
19496
|
-
videoModel:
|
|
19497
|
-
|
|
19498
|
-
"wan",
|
|
19499
|
-
"bytedance",
|
|
19500
|
-
"seedance",
|
|
19501
|
-
"grok",
|
|
19502
|
-
"kling"
|
|
19503
|
-
]).optional(),
|
|
19504
|
-
videoTier: z73.enum([
|
|
20522
|
+
videoModel: TaskVideoModelEnum.optional(),
|
|
20523
|
+
videoTier: z77.enum([
|
|
19505
20524
|
"legacy",
|
|
19506
20525
|
"standard",
|
|
19507
20526
|
"pro",
|
|
19508
20527
|
"single_call"
|
|
19509
20528
|
]).optional(),
|
|
19510
|
-
videoResolution:
|
|
20529
|
+
videoResolution: z77.enum([
|
|
19511
20530
|
"480p",
|
|
19512
20531
|
"720p"
|
|
19513
20532
|
]).optional(),
|
|
19514
20533
|
narrativeStyle: NarrativeStyleEnum.optional(),
|
|
19515
|
-
pauseForVideoScriptReview:
|
|
19516
|
-
pendingUserAction:
|
|
19517
|
-
kind:
|
|
19518
|
-
candidateGrouping:
|
|
19519
|
-
selectedImageUrls:
|
|
19520
|
-
proposedProductNames:
|
|
20534
|
+
pauseForVideoScriptReview: z77.boolean().optional(),
|
|
20535
|
+
pendingUserAction: z77.object({
|
|
20536
|
+
kind: z77.string(),
|
|
20537
|
+
candidateGrouping: z77.string().optional(),
|
|
20538
|
+
selectedImageUrls: z77.array(z77.string()).optional(),
|
|
20539
|
+
proposedProductNames: z77.array(z77.string()).optional(),
|
|
19521
20540
|
// Adaptive reference questions are server-owned. Echo only the evidence
|
|
19522
20541
|
// identity and the choices shown by the current snapshot; never accept a
|
|
19523
20542
|
// client-supplied reference set as authorization.
|
|
19524
|
-
options:
|
|
19525
|
-
mode:
|
|
20543
|
+
options: z77.array(ProductReferenceUserChoiceEnum).max(4).optional(),
|
|
20544
|
+
mode: z77.enum([
|
|
19526
20545
|
"question",
|
|
19527
20546
|
"visual_recovery"
|
|
19528
20547
|
]).optional(),
|
|
19529
20548
|
/** Hand-mirrored from packages/types result.ts — see CLAUDE.md § MCP Server Sync. */
|
|
19530
|
-
canKeepSelection:
|
|
19531
|
-
decisionKey:
|
|
19532
|
-
questionOrdinal:
|
|
19533
|
-
questionCeiling:
|
|
19534
|
-
|
|
19535
|
-
|
|
20549
|
+
canKeepSelection: z77.boolean().optional(),
|
|
20550
|
+
decisionKey: z77.string().optional(),
|
|
20551
|
+
questionOrdinal: z77.number().int().min(1).max(5).optional(),
|
|
20552
|
+
questionCeiling: z77.union([
|
|
20553
|
+
z77.literal(3),
|
|
20554
|
+
z77.literal(5)
|
|
19536
20555
|
]).optional(),
|
|
19537
|
-
referenceEvidenceVersion:
|
|
19538
|
-
expectedEvidenceHash:
|
|
19539
|
-
question:
|
|
19540
|
-
prompt:
|
|
19541
|
-
helpText:
|
|
19542
|
-
options:
|
|
20556
|
+
referenceEvidenceVersion: z77.number().int().positive().optional(),
|
|
20557
|
+
expectedEvidenceHash: z77.string().optional(),
|
|
20558
|
+
question: z77.object({
|
|
20559
|
+
prompt: z77.string().min(1).max(240),
|
|
20560
|
+
helpText: z77.string().min(1).max(300).optional(),
|
|
20561
|
+
options: z77.array(z77.object({
|
|
19543
20562
|
value: ProductReferenceUserChoiceEnum,
|
|
19544
|
-
label:
|
|
19545
|
-
description:
|
|
20563
|
+
label: z77.string().min(1).max(120),
|
|
20564
|
+
description: z77.string().min(1).max(240).optional()
|
|
19546
20565
|
})).min(2).max(4)
|
|
19547
20566
|
}).optional(),
|
|
19548
20567
|
scope: ProductReferenceScopeEnum.optional(),
|
|
19549
|
-
scopeKey:
|
|
19550
|
-
scopeSnapshotId:
|
|
19551
|
-
scopeHash:
|
|
19552
|
-
baseReferenceSetHash:
|
|
19553
|
-
phase:
|
|
20568
|
+
scopeKey: z77.string().optional(),
|
|
20569
|
+
scopeSnapshotId: z77.string().uuid().optional(),
|
|
20570
|
+
scopeHash: z77.string().optional(),
|
|
20571
|
+
baseReferenceSetHash: z77.string().optional(),
|
|
20572
|
+
phase: z77.enum([
|
|
19554
20573
|
"preparation",
|
|
19555
20574
|
"final"
|
|
19556
20575
|
]).optional(),
|
|
19557
|
-
selectedRefKeys:
|
|
19558
|
-
allowedDecisions:
|
|
19559
|
-
previews:
|
|
19560
|
-
refKey:
|
|
20576
|
+
selectedRefKeys: z77.array(z77.string().min(1).max(200)).optional(),
|
|
20577
|
+
allowedDecisions: z77.array(ProductReferenceResolutionDecisionEnum).optional(),
|
|
20578
|
+
previews: z77.array(z77.object({
|
|
20579
|
+
refKey: z77.string().min(1).max(200)
|
|
19561
20580
|
}).passthrough()).optional(),
|
|
19562
|
-
customAngleDraft:
|
|
19563
|
-
painPointsDraft:
|
|
19564
|
-
benefitsDraft:
|
|
19565
|
-
featuresDraft:
|
|
19566
|
-
angleOptions:
|
|
19567
|
-
index:
|
|
19568
|
-
angle:
|
|
20581
|
+
customAngleDraft: z77.string().optional(),
|
|
20582
|
+
painPointsDraft: z77.string().optional(),
|
|
20583
|
+
benefitsDraft: z77.string().optional(),
|
|
20584
|
+
featuresDraft: z77.array(z77.string()).optional(),
|
|
20585
|
+
angleOptions: z77.array(z77.object({
|
|
20586
|
+
index: z77.number().int().nonnegative(),
|
|
20587
|
+
angle: z77.string().min(1)
|
|
19569
20588
|
}).passthrough()).min(1).max(5).optional(),
|
|
19570
20589
|
// Recommendation consumed only when the caller explicitly sends
|
|
19571
20590
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
@@ -19575,17 +20594,17 @@ var StatusForResolveSchema = z73.object({
|
|
|
19575
20594
|
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
19576
20595
|
// dropped here, so the approval handler below could not echo it back on the
|
|
19577
20596
|
// PATCH — the selection looked accepted and reached no render.
|
|
19578
|
-
angles:
|
|
19579
|
-
id:
|
|
19580
|
-
content:
|
|
19581
|
-
creativeDirection:
|
|
19582
|
-
actor:
|
|
19583
|
-
actorId:
|
|
20597
|
+
angles: z77.array(z77.object({
|
|
20598
|
+
id: z77.string().uuid(),
|
|
20599
|
+
content: z77.string(),
|
|
20600
|
+
creativeDirection: z77.string(),
|
|
20601
|
+
actor: z77.string(),
|
|
20602
|
+
actorId: z77.string().nullable().optional()
|
|
19584
20603
|
}).passthrough()).optional(),
|
|
19585
|
-
panels:
|
|
19586
|
-
beatId:
|
|
19587
|
-
voiceover:
|
|
19588
|
-
visualDirection:
|
|
20604
|
+
panels: z77.array(z77.object({
|
|
20605
|
+
beatId: z77.string(),
|
|
20606
|
+
voiceover: z77.string(),
|
|
20607
|
+
visualDirection: z77.string()
|
|
19589
20608
|
}).passthrough()).optional()
|
|
19590
20609
|
}).optional()
|
|
19591
20610
|
}).passthrough();
|
|
@@ -19784,10 +20803,25 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
19784
20803
|
};
|
|
19785
20804
|
const r = await api.postMultipart("/v1/ingest", {
|
|
19786
20805
|
images: parts
|
|
19787
|
-
}, fields
|
|
20806
|
+
}, fields, {
|
|
20807
|
+
// LLM hosts retry paid tools with rephrased arguments — the API's
|
|
20808
|
+
// byte-fingerprint can't see the duplicate, this key can. Derived
|
|
20809
|
+
// from the semantic inputs, not the wire fields, for the same
|
|
20810
|
+
// reason (see request-key.ts, which also canonicalizes the
|
|
20811
|
+
// order-insignificant `outputs` set).
|
|
20812
|
+
idempotencyKey: derivePaidRequestKey({
|
|
20813
|
+
fingerprint: api.fingerprint(),
|
|
20814
|
+
tool: "create_product_task",
|
|
20815
|
+
input,
|
|
20816
|
+
nowMs: Date.now()
|
|
20817
|
+
})
|
|
20818
|
+
});
|
|
19788
20819
|
return asText({
|
|
19789
20820
|
taskId: r.taskId,
|
|
19790
|
-
status: "queued",
|
|
20821
|
+
status: r.status ?? "queued",
|
|
20822
|
+
...r.replayed ? {
|
|
20823
|
+
replayed: true
|
|
20824
|
+
} : {},
|
|
19791
20825
|
estimatedSeconds: 300,
|
|
19792
20826
|
outputs: selectedOutputs,
|
|
19793
20827
|
outputMode: "single_task",
|
|
@@ -19824,26 +20858,56 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
19824
20858
|
assets: []
|
|
19825
20859
|
},
|
|
19826
20860
|
appDemoVideos: []
|
|
20861
|
+
}, {
|
|
20862
|
+
// The 2026-09-25 incident: four retries of this exact tool created
|
|
20863
|
+
// four charged service tasks because the host rephrased
|
|
20864
|
+
// `additionalContext` and flipped the landing between `/` and
|
|
20865
|
+
// `/es`. The derived key is stable across both rewrites (see
|
|
20866
|
+
// request-key.ts).
|
|
20867
|
+
idempotencyKey: derivePaidRequestKey({
|
|
20868
|
+
fingerprint: api.fingerprint(),
|
|
20869
|
+
tool: "create_service_video",
|
|
20870
|
+
input,
|
|
20871
|
+
nowMs: Date.now()
|
|
20872
|
+
})
|
|
19827
20873
|
});
|
|
19828
20874
|
const angles = await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles`, {});
|
|
19829
20875
|
const first = angles[0];
|
|
20876
|
+
const replayed = created.replayed === true;
|
|
19830
20877
|
if (input.reviewMode === "automatic") {
|
|
19831
20878
|
if (!first) throw new Error("ProductMaker no pudo crear un concepto para este servicio.");
|
|
19832
|
-
|
|
19833
|
-
|
|
19834
|
-
|
|
19835
|
-
|
|
19836
|
-
|
|
20879
|
+
if (!(replayed && created.status !== "queued")) {
|
|
20880
|
+
await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles/select`, {
|
|
20881
|
+
angleIds: [
|
|
20882
|
+
first.id
|
|
20883
|
+
]
|
|
20884
|
+
});
|
|
20885
|
+
}
|
|
19837
20886
|
return asText({
|
|
19838
20887
|
taskId: created.taskId,
|
|
19839
|
-
status: "processing",
|
|
20888
|
+
status: replayed ? created.status ?? "processing" : "processing",
|
|
20889
|
+
...replayed ? {
|
|
20890
|
+
replayed: true
|
|
20891
|
+
} : {},
|
|
19840
20892
|
reviewMode: input.reviewMode,
|
|
19841
20893
|
selectedConcept: first
|
|
19842
20894
|
});
|
|
19843
20895
|
}
|
|
20896
|
+
if (replayed && created.status !== "queued") {
|
|
20897
|
+
return asText({
|
|
20898
|
+
taskId: created.taskId,
|
|
20899
|
+
status: created.status,
|
|
20900
|
+
replayed: true,
|
|
20901
|
+
reviewMode: input.reviewMode,
|
|
20902
|
+
angles
|
|
20903
|
+
});
|
|
20904
|
+
}
|
|
19844
20905
|
return asText({
|
|
19845
20906
|
taskId: created.taskId,
|
|
19846
20907
|
status: "waiting_for_user",
|
|
20908
|
+
...replayed ? {
|
|
20909
|
+
replayed: true
|
|
20910
|
+
} : {},
|
|
19847
20911
|
reviewMode: input.reviewMode,
|
|
19848
20912
|
pendingUserAction: {
|
|
19849
20913
|
kind: "service_angle_approval",
|
|
@@ -20419,8 +21483,7 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
20419
21483
|
__name(registerTaskTools, "registerTaskTools");
|
|
20420
21484
|
|
|
20421
21485
|
// src/tools/creatives.ts
|
|
20422
|
-
import {
|
|
20423
|
-
import { z as z74 } from "zod";
|
|
21486
|
+
import { z as z78 } from "zod";
|
|
20424
21487
|
var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
|
|
20425
21488
|
var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
|
|
20426
21489
|
var generateImageCreativesGuidance = getProductMakerCapabilityModelGuidance("generate_image_creatives");
|
|
@@ -20430,70 +21493,70 @@ var CreativeRefs = {
|
|
|
20430
21493
|
images: ImagesInput,
|
|
20431
21494
|
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.'),
|
|
20432
21495
|
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.'),
|
|
20433
|
-
price:
|
|
20434
|
-
offer:
|
|
20435
|
-
compareAtPrice:
|
|
20436
|
-
productContext:
|
|
20437
|
-
productLabel:
|
|
20438
|
-
buyerPersona:
|
|
20439
|
-
isCombo:
|
|
20440
|
-
competitorScanEnabled:
|
|
20441
|
-
brandId:
|
|
21496
|
+
price: z78.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.'),
|
|
21497
|
+
offer: z78.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.'),
|
|
21498
|
+
compareAtPrice: z78.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
|
|
21499
|
+
productContext: z78.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."),
|
|
21500
|
+
productLabel: z78.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.'),
|
|
21501
|
+
buyerPersona: z78.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.'),
|
|
21502
|
+
isCombo: z78.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
21503
|
+
competitorScanEnabled: z78.boolean().optional().describe("Opcional. Debe enviarse true solo despu\xE9s de que el usuario acepte estudiar la competencia (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. Omitir no autoriza el estudio profundo."),
|
|
21504
|
+
brandId: z78.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.")
|
|
20442
21505
|
};
|
|
20443
|
-
var VideoPresenter =
|
|
20444
|
-
|
|
20445
|
-
mode:
|
|
21506
|
+
var VideoPresenter = z78.discriminatedUnion("mode", [
|
|
21507
|
+
z78.object({
|
|
21508
|
+
mode: z78.literal("none")
|
|
20446
21509
|
}).strict(),
|
|
20447
|
-
|
|
20448
|
-
mode:
|
|
20449
|
-
actorId:
|
|
21510
|
+
z78.object({
|
|
21511
|
+
mode: z78.literal("saved_actor"),
|
|
21512
|
+
actorId: z78.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
|
|
20450
21513
|
}).strict(),
|
|
20451
|
-
|
|
20452
|
-
mode:
|
|
20453
|
-
description:
|
|
21514
|
+
z78.object({
|
|
21515
|
+
mode: z78.literal("described"),
|
|
21516
|
+
description: z78.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
|
|
20454
21517
|
}).strict()
|
|
20455
21518
|
]);
|
|
20456
|
-
var VideoCreativeSetup =
|
|
20457
|
-
|
|
20458
|
-
mode:
|
|
21519
|
+
var VideoCreativeSetup = z78.discriminatedUnion("mode", [
|
|
21520
|
+
z78.object({
|
|
21521
|
+
mode: z78.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
|
|
20459
21522
|
}).strict(),
|
|
20460
|
-
|
|
20461
|
-
mode:
|
|
21523
|
+
z78.object({
|
|
21524
|
+
mode: z78.literal("guided"),
|
|
20462
21525
|
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. "problem_solution" requiere 30s.'),
|
|
20463
|
-
buyerPersona:
|
|
21526
|
+
buyerPersona: z78.string().trim().min(1).max(360),
|
|
20464
21527
|
presenter: VideoPresenter,
|
|
20465
|
-
creativeDirection:
|
|
21528
|
+
creativeDirection: z78.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
|
|
20466
21529
|
}).strict()
|
|
20467
21530
|
]);
|
|
20468
|
-
var VideoInput =
|
|
21531
|
+
var VideoInput = z78.object({
|
|
20469
21532
|
images: CreativeRefs.images.optional(),
|
|
20470
21533
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
20471
|
-
title:
|
|
20472
|
-
primaryAngle:
|
|
21534
|
+
title: z78.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
21535
|
+
primaryAngle: z78.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".'),
|
|
20473
21536
|
language: CreativeRefs.language,
|
|
20474
21537
|
country: CreativeRefs.country,
|
|
20475
|
-
aspectRatio:
|
|
21538
|
+
aspectRatio: z78.enum([
|
|
20476
21539
|
"9:16",
|
|
20477
21540
|
"16:9",
|
|
20478
21541
|
"Auto"
|
|
20479
21542
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
20480
|
-
durationSeconds:
|
|
20481
|
-
videoAngleCount:
|
|
21543
|
+
durationSeconds: z78.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."),
|
|
21544
|
+
videoAngleCount: z78.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."),
|
|
20482
21545
|
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.'),
|
|
20483
21546
|
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."),
|
|
20484
21547
|
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.'),
|
|
20485
21548
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
|
|
20486
21549
|
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.'),
|
|
20487
|
-
features:
|
|
21550
|
+
features: z78.array(z78.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
|
|
20488
21551
|
price: CreativeRefs.price,
|
|
20489
21552
|
offer: CreativeRefs.offer,
|
|
20490
21553
|
productContext: CreativeRefs.productContext,
|
|
20491
21554
|
productLabel: CreativeRefs.productLabel,
|
|
20492
|
-
cta:
|
|
20493
|
-
scriptNotes:
|
|
21555
|
+
cta: z78.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.'),
|
|
21556
|
+
scriptNotes: z78.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.'),
|
|
20494
21557
|
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.'),
|
|
20495
21558
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
20496
|
-
replicateWinnerWhenFound:
|
|
21559
|
+
replicateWinnerWhenFound: z78.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."),
|
|
20497
21560
|
brandId: CreativeRefs.brandId
|
|
20498
21561
|
}).strict();
|
|
20499
21562
|
var FreeVideoQualityEnum = VideoQualityEnum;
|
|
@@ -20511,7 +21574,7 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
20511
21574
|
length: 29
|
|
20512
21575
|
}, (_, index) => index + 2)
|
|
20513
21576
|
};
|
|
20514
|
-
var FreeVideoModelIdEnum =
|
|
21577
|
+
var FreeVideoModelIdEnum = z78.enum([
|
|
20515
21578
|
"grok-imagine-video-1-5-preview",
|
|
20516
21579
|
"minimax-h3/reference-to-video",
|
|
20517
21580
|
"wan/3-0-video",
|
|
@@ -20522,70 +21585,65 @@ var FREE_SELECTABLE_MODEL_IDS = [
|
|
|
20522
21585
|
"minimax-h3/reference-to-video",
|
|
20523
21586
|
"wan/3-0-video"
|
|
20524
21587
|
];
|
|
20525
|
-
var FreeVideoInput =
|
|
21588
|
+
var FreeVideoInput = z78.object({
|
|
20526
21589
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
20527
|
-
rawPrompt:
|
|
21590
|
+
rawPrompt: z78.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."),
|
|
20528
21591
|
language: CreativeRefs.language,
|
|
20529
21592
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan).'),
|
|
20530
21593
|
// Retired in MCP 7.0.0 (Task 11): kept in the schema, accepted, and
|
|
20531
21594
|
// parsed ONLY so the handler can reject it with a message naming its
|
|
20532
21595
|
// replacement — dropping it silently would turn a caller's explicit
|
|
20533
21596
|
// Pro++ pick into a quiet fallback to Wan.
|
|
20534
|
-
freeProModel:
|
|
21597
|
+
freeProModel: z78.enum([
|
|
20535
21598
|
"wan",
|
|
20536
21599
|
"seedance"
|
|
20537
21600
|
]).optional().describe("ELIMINADO en MCP 7.0.0 \u2014 usa `modelId`. Enviar este campo produce un error."),
|
|
20538
21601
|
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.'),
|
|
20539
|
-
durationSeconds:
|
|
21602
|
+
durationSeconds: z78.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."),
|
|
20540
21603
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
20541
|
-
generateAiMusic:
|
|
20542
|
-
addSubtitles:
|
|
20543
|
-
idempotencyKey:
|
|
21604
|
+
generateAiMusic: z78.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
21605
|
+
addSubtitles: z78.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
21606
|
+
idempotencyKey: z78.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.")
|
|
20544
21607
|
}).strict();
|
|
20545
21608
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
20546
|
-
var ImageCreativeSetup =
|
|
20547
|
-
|
|
20548
|
-
mode:
|
|
21609
|
+
var ImageCreativeSetup = z78.discriminatedUnion("mode", [
|
|
21610
|
+
z78.object({
|
|
21611
|
+
mode: z78.literal("automatic")
|
|
20549
21612
|
}).strict(),
|
|
20550
|
-
|
|
20551
|
-
mode:
|
|
20552
|
-
creativeArchetype:
|
|
21613
|
+
z78.object({
|
|
21614
|
+
mode: z78.literal("guided"),
|
|
21615
|
+
creativeArchetype: z78.union([
|
|
20553
21616
|
ImageCreativeArchetypeEnum,
|
|
20554
|
-
|
|
21617
|
+
z78.literal("ai_decides")
|
|
20555
21618
|
]),
|
|
20556
|
-
visualTreatment:
|
|
21619
|
+
visualTreatment: z78.union([
|
|
20557
21620
|
ImageCreativeVisualTreatmentEnum,
|
|
20558
|
-
|
|
21621
|
+
z78.literal("ai_decides")
|
|
20559
21622
|
]),
|
|
20560
21623
|
aspectRatio: ImageCreativeAspectRatioEnum,
|
|
20561
|
-
nVariants:
|
|
20562
|
-
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
21624
|
+
nVariants: z78.union([
|
|
21625
|
+
z78.literal(1),
|
|
21626
|
+
z78.literal(2),
|
|
21627
|
+
z78.literal(3),
|
|
21628
|
+
z78.literal(4),
|
|
21629
|
+
z78.literal(6)
|
|
20567
21630
|
]),
|
|
20568
|
-
imageModel:
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
|
|
20572
|
-
|
|
20573
|
-
|
|
20574
|
-
|
|
20575
|
-
|
|
20576
|
-
|
|
20577
|
-
productContext: z74.string().trim().min(1).max(3e3).nullable(),
|
|
20578
|
-
creativeDirection: z74.string().trim().min(1).max(500).nullable(),
|
|
20579
|
-
brandColors: z74.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.'),
|
|
20580
|
-
headline: z74.string().trim().min(1).max(120).nullable(),
|
|
20581
|
-
subheadline: z74.string().trim().min(1).max(160).nullable()
|
|
21631
|
+
imageModel: z78.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.'),
|
|
21632
|
+
styleMix: z78.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
|
|
21633
|
+
generateAdCopy: z78.boolean(),
|
|
21634
|
+
competitorScanEnabled: z78.boolean(),
|
|
21635
|
+
productContext: z78.string().trim().min(1).max(3e3).nullable(),
|
|
21636
|
+
creativeDirection: z78.string().trim().min(1).max(500).nullable(),
|
|
21637
|
+
brandColors: z78.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.'),
|
|
21638
|
+
headline: z78.string().trim().min(1).max(120).nullable(),
|
|
21639
|
+
subheadline: z78.string().trim().min(1).max(160).nullable()
|
|
20582
21640
|
}).strict()
|
|
20583
21641
|
]).superRefine((setup, context) => {
|
|
20584
21642
|
if (setup.mode !== "guided") return;
|
|
20585
21643
|
const needsStyleMix = setup.nVariants >= 2 && setup.nVariants <= 4;
|
|
20586
21644
|
if (needsStyleMix && setup.styleMix === null) {
|
|
20587
21645
|
context.addIssue({
|
|
20588
|
-
code:
|
|
21646
|
+
code: z78.ZodIssueCode.custom,
|
|
20589
21647
|
path: [
|
|
20590
21648
|
"styleMix"
|
|
20591
21649
|
],
|
|
@@ -20594,16 +21652,16 @@ var ImageCreativeSetup = z74.discriminatedUnion("mode", [
|
|
|
20594
21652
|
}
|
|
20595
21653
|
if (!needsStyleMix && setup.styleMix !== null) {
|
|
20596
21654
|
context.addIssue({
|
|
20597
|
-
code:
|
|
21655
|
+
code: z78.ZodIssueCode.custom,
|
|
20598
21656
|
path: [
|
|
20599
21657
|
"styleMix"
|
|
20600
21658
|
],
|
|
20601
21659
|
message: "styleMix debe ser null cuando nVariants es 1 o 6."
|
|
20602
21660
|
});
|
|
20603
21661
|
}
|
|
20604
|
-
if ((setup.imageModel === "
|
|
21662
|
+
if (imageResolutionOf(setup.imageModel) === "2k" && setup.aspectRatio === "4:5") {
|
|
20605
21663
|
context.addIssue({
|
|
20606
|
-
code:
|
|
21664
|
+
code: z78.ZodIssueCode.custom,
|
|
20607
21665
|
path: [
|
|
20608
21666
|
"aspectRatio"
|
|
20609
21667
|
],
|
|
@@ -20611,53 +21669,53 @@ var ImageCreativeSetup = z74.discriminatedUnion("mode", [
|
|
|
20611
21669
|
});
|
|
20612
21670
|
}
|
|
20613
21671
|
});
|
|
20614
|
-
var LandingCreativeSetup =
|
|
20615
|
-
|
|
20616
|
-
mode:
|
|
21672
|
+
var LandingCreativeSetup = z78.discriminatedUnion("mode", [
|
|
21673
|
+
z78.object({
|
|
21674
|
+
mode: z78.literal("automatic")
|
|
20617
21675
|
}).strict(),
|
|
20618
|
-
|
|
20619
|
-
mode:
|
|
21676
|
+
z78.object({
|
|
21677
|
+
mode: z78.literal("guided"),
|
|
20620
21678
|
landingStrategy: LandingStrategyEnum,
|
|
20621
21679
|
landingImageFormat: LandingImageFormatEnum,
|
|
20622
|
-
competitorScanEnabled:
|
|
20623
|
-
paymentMethods:
|
|
20624
|
-
productContext:
|
|
20625
|
-
creativeDirection:
|
|
20626
|
-
headline:
|
|
20627
|
-
subheadline:
|
|
21680
|
+
competitorScanEnabled: z78.boolean(),
|
|
21681
|
+
paymentMethods: z78.string().trim().min(1).max(240).nullable(),
|
|
21682
|
+
productContext: z78.string().trim().min(1).max(3e3).nullable(),
|
|
21683
|
+
creativeDirection: z78.string().trim().min(1).max(500).nullable(),
|
|
21684
|
+
headline: z78.string().trim().min(1).max(120).nullable(),
|
|
21685
|
+
subheadline: z78.string().trim().min(1).max(160).nullable()
|
|
20628
21686
|
}).strict()
|
|
20629
21687
|
]);
|
|
20630
|
-
var ImageInput =
|
|
21688
|
+
var ImageInput = z78.object({
|
|
20631
21689
|
images: CreativeRefs.images.optional(),
|
|
20632
21690
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
20633
|
-
productTitle:
|
|
20634
|
-
primaryAngle:
|
|
21691
|
+
productTitle: z78.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
21692
|
+
primaryAngle: z78.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.'),
|
|
20635
21693
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
20636
21694
|
language: CreativeRefs.language,
|
|
20637
21695
|
country: CreativeRefs.country,
|
|
20638
21696
|
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."),
|
|
20639
21697
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
20640
|
-
features:
|
|
21698
|
+
features: z78.array(z78.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
20641
21699
|
price: CreativeRefs.price,
|
|
20642
|
-
omitPrice:
|
|
21700
|
+
omitPrice: z78.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."),
|
|
20643
21701
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
20644
21702
|
offer: CreativeRefs.offer,
|
|
20645
21703
|
productLabel: CreativeRefs.productLabel,
|
|
20646
21704
|
isCombo: CreativeRefs.isCombo,
|
|
20647
21705
|
brandId: CreativeRefs.brandId
|
|
20648
21706
|
});
|
|
20649
|
-
var EditImageCreativeInput =
|
|
20650
|
-
taskId:
|
|
20651
|
-
variantIndex:
|
|
20652
|
-
instruction:
|
|
21707
|
+
var EditImageCreativeInput = z78.object({
|
|
21708
|
+
taskId: z78.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
|
|
21709
|
+
variantIndex: z78.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
|
|
21710
|
+
instruction: z78.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".')
|
|
20653
21711
|
});
|
|
20654
|
-
var LandingInput =
|
|
21712
|
+
var LandingInput = z78.object({
|
|
20655
21713
|
images: CreativeRefs.images.optional(),
|
|
20656
21714
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
20657
21715
|
language: CreativeRefs.language,
|
|
20658
21716
|
country: CreativeRefs.country,
|
|
20659
21717
|
creativeSetup: LandingCreativeSetup.describe("Configuraci\xF3n obligatoria de landing cl\xE1sica equivalente al wizard web. No expone landing editable/code mientras siga siendo admin-only."),
|
|
20660
|
-
salesAngle:
|
|
21718
|
+
salesAngle: z78.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.'),
|
|
20661
21719
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
20662
21720
|
price: CreativeRefs.price,
|
|
20663
21721
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -20676,7 +21734,7 @@ function resolveImageSetup(setup) {
|
|
|
20676
21734
|
},
|
|
20677
21735
|
aspectRatio: setup.aspectRatio,
|
|
20678
21736
|
nVariants: setup.nVariants,
|
|
20679
|
-
imageModel: setup.imageModel,
|
|
21737
|
+
imageModel: canonicalImageModelInput(setup.imageModel),
|
|
20680
21738
|
...setup.nVariants === 6 ? {
|
|
20681
21739
|
styleMix: true
|
|
20682
21740
|
} : setup.styleMix === null ? {} : {
|
|
@@ -20767,7 +21825,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
20767
21825
|
addSubtitles: input.addSubtitles
|
|
20768
21826
|
},
|
|
20769
21827
|
allowEmptyImages: true,
|
|
20770
|
-
|
|
21828
|
+
// Always derived — never `input.idempotencyKey ??`: a host that
|
|
21829
|
+
// mints a fresh UUID per retry would defeat the dedupe entirely.
|
|
21830
|
+
// The semantic key is also a strictly better retry identity: a
|
|
21831
|
+
// reused caller key still lands on it (see request-key.ts).
|
|
21832
|
+
idempotencyKey: derivePaidRequestKey({
|
|
21833
|
+
fingerprint: api.fingerprint(),
|
|
21834
|
+
tool: "generate_free_video",
|
|
21835
|
+
input,
|
|
21836
|
+
nowMs: Date.now()
|
|
21837
|
+
}),
|
|
20771
21838
|
transport
|
|
20772
21839
|
});
|
|
20773
21840
|
} catch (e) {
|
|
@@ -20832,6 +21899,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
20832
21899
|
path: "/v1/creatives/videos",
|
|
20833
21900
|
estimatedSeconds: 300,
|
|
20834
21901
|
input: wirePayload,
|
|
21902
|
+
idempotencyKey: derivePaidRequestKey({
|
|
21903
|
+
fingerprint: api.fingerprint(),
|
|
21904
|
+
tool: "generate_video_creative",
|
|
21905
|
+
input,
|
|
21906
|
+
nowMs: Date.now()
|
|
21907
|
+
}),
|
|
20835
21908
|
transport
|
|
20836
21909
|
});
|
|
20837
21910
|
} catch (e) {
|
|
@@ -20866,6 +21939,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
20866
21939
|
pauseForAngleReview: true
|
|
20867
21940
|
}
|
|
20868
21941
|
},
|
|
21942
|
+
idempotencyKey: derivePaidRequestKey({
|
|
21943
|
+
fingerprint: api.fingerprint(),
|
|
21944
|
+
tool: "generate_image_creatives",
|
|
21945
|
+
input,
|
|
21946
|
+
nowMs: Date.now()
|
|
21947
|
+
}),
|
|
20869
21948
|
transport
|
|
20870
21949
|
});
|
|
20871
21950
|
} catch (e) {
|
|
@@ -20909,6 +21988,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
20909
21988
|
pauseForAngleReview: true
|
|
20910
21989
|
}
|
|
20911
21990
|
},
|
|
21991
|
+
idempotencyKey: derivePaidRequestKey({
|
|
21992
|
+
fingerprint: api.fingerprint(),
|
|
21993
|
+
tool: "generate_landing",
|
|
21994
|
+
input,
|
|
21995
|
+
nowMs: Date.now()
|
|
21996
|
+
}),
|
|
20912
21997
|
transport
|
|
20913
21998
|
});
|
|
20914
21999
|
} catch (e) {
|
|
@@ -20919,10 +22004,10 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
20919
22004
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
20920
22005
|
|
|
20921
22006
|
// src/tools/creative-variants.ts
|
|
20922
|
-
import { z as
|
|
22007
|
+
import { z as z79 } from "zod";
|
|
20923
22008
|
var estimateCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("estimate_creative_variant");
|
|
20924
22009
|
var createCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("create_creative_variant");
|
|
20925
|
-
var LandingSectionKeyEnum =
|
|
22010
|
+
var LandingSectionKeyEnum = z79.enum([
|
|
20926
22011
|
"problem_hook",
|
|
20927
22012
|
"problem_cost",
|
|
20928
22013
|
"solution_mechanism",
|
|
@@ -20962,24 +22047,24 @@ var LandingSectionKeyEnum = z75.enum([
|
|
|
20962
22047
|
"lifestyle_fit",
|
|
20963
22048
|
"join_lifestyle"
|
|
20964
22049
|
]);
|
|
20965
|
-
var CreativeVariantKindEnum =
|
|
22050
|
+
var CreativeVariantKindEnum = z79.enum([
|
|
20966
22051
|
"video",
|
|
20967
22052
|
"image",
|
|
20968
22053
|
"landing"
|
|
20969
22054
|
]);
|
|
20970
|
-
var EstimateCreativeVariantInputBase =
|
|
20971
|
-
sourceTaskId:
|
|
22055
|
+
var EstimateCreativeVariantInputBase = z79.object({
|
|
22056
|
+
sourceTaskId: z79.string().uuid().describe("ID de la tarea original completada."),
|
|
20972
22057
|
kind: CreativeVariantKindEnum,
|
|
20973
22058
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
20974
22059
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
20975
|
-
nVariants:
|
|
22060
|
+
nVariants: z79.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
20976
22061
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
20977
|
-
instructions:
|
|
22062
|
+
instructions: z79.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.")
|
|
20978
22063
|
}).strict();
|
|
20979
22064
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
20980
22065
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
20981
22066
|
ctx.addIssue({
|
|
20982
|
-
code:
|
|
22067
|
+
code: z79.ZodIssueCode.custom,
|
|
20983
22068
|
path: [
|
|
20984
22069
|
"nVariants"
|
|
20985
22070
|
],
|
|
@@ -20989,7 +22074,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
20989
22074
|
if (input.kind !== "video") {
|
|
20990
22075
|
if (input.videoPreset !== void 0) {
|
|
20991
22076
|
ctx.addIssue({
|
|
20992
|
-
code:
|
|
22077
|
+
code: z79.ZodIssueCode.custom,
|
|
20993
22078
|
path: [
|
|
20994
22079
|
"videoPreset"
|
|
20995
22080
|
],
|
|
@@ -20998,7 +22083,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
20998
22083
|
}
|
|
20999
22084
|
if (input.videoResolution !== void 0) {
|
|
21000
22085
|
ctx.addIssue({
|
|
21001
|
-
code:
|
|
22086
|
+
code: z79.ZodIssueCode.custom,
|
|
21002
22087
|
path: [
|
|
21003
22088
|
"videoResolution"
|
|
21004
22089
|
],
|
|
@@ -21008,7 +22093,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
21008
22093
|
}
|
|
21009
22094
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
21010
22095
|
ctx.addIssue({
|
|
21011
|
-
code:
|
|
22096
|
+
code: z79.ZodIssueCode.custom,
|
|
21012
22097
|
path: [
|
|
21013
22098
|
"nVariants"
|
|
21014
22099
|
],
|
|
@@ -21018,7 +22103,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
21018
22103
|
if (input.kind !== "landing") {
|
|
21019
22104
|
if (input.sectionKey !== void 0) {
|
|
21020
22105
|
ctx.addIssue({
|
|
21021
|
-
code:
|
|
22106
|
+
code: z79.ZodIssueCode.custom,
|
|
21022
22107
|
path: [
|
|
21023
22108
|
"sectionKey"
|
|
21024
22109
|
],
|
|
@@ -21027,7 +22112,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
21027
22112
|
}
|
|
21028
22113
|
if (input.instructions !== void 0) {
|
|
21029
22114
|
ctx.addIssue({
|
|
21030
|
-
code:
|
|
22115
|
+
code: z79.ZodIssueCode.custom,
|
|
21031
22116
|
path: [
|
|
21032
22117
|
"instructions"
|
|
21033
22118
|
],
|
|
@@ -21036,31 +22121,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
21036
22121
|
}
|
|
21037
22122
|
}
|
|
21038
22123
|
});
|
|
21039
|
-
var CreateCreativeVariantInputBase =
|
|
21040
|
-
sourceTaskId:
|
|
22124
|
+
var CreateCreativeVariantInputBase = z79.object({
|
|
22125
|
+
sourceTaskId: z79.string().uuid().describe("ID de la tarea original completada."),
|
|
21041
22126
|
kind: CreativeVariantKindEnum,
|
|
21042
|
-
confirm:
|
|
21043
|
-
clientRequestId:
|
|
21044
|
-
angle:
|
|
21045
|
-
buyerPersona:
|
|
21046
|
-
scriptNotes:
|
|
21047
|
-
creativeDirection:
|
|
22127
|
+
confirm: z79.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
22128
|
+
clientRequestId: z79.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."),
|
|
22129
|
+
angle: z79.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
22130
|
+
buyerPersona: z79.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
22131
|
+
scriptNotes: z79.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
22132
|
+
creativeDirection: z79.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
21048
22133
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
21049
22134
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
21050
|
-
generateAiMusic:
|
|
21051
|
-
nVariants:
|
|
22135
|
+
generateAiMusic: z79.boolean().optional().describe("Opcional para video."),
|
|
22136
|
+
nVariants: z79.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
21052
22137
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
21053
22138
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
21054
22139
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
21055
22140
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
21056
|
-
styleMix:
|
|
22141
|
+
styleMix: z79.boolean().optional().describe("Opcional para imagen."),
|
|
21057
22142
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
21058
|
-
instructions:
|
|
22143
|
+
instructions: z79.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.")
|
|
21059
22144
|
}).strict();
|
|
21060
22145
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
21061
22146
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
21062
22147
|
ctx.addIssue({
|
|
21063
|
-
code:
|
|
22148
|
+
code: z79.ZodIssueCode.custom,
|
|
21064
22149
|
path: [
|
|
21065
22150
|
"angle"
|
|
21066
22151
|
],
|
|
@@ -21069,7 +22154,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
21069
22154
|
}
|
|
21070
22155
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
21071
22156
|
ctx.addIssue({
|
|
21072
|
-
code:
|
|
22157
|
+
code: z79.ZodIssueCode.custom,
|
|
21073
22158
|
path: [
|
|
21074
22159
|
"instructions"
|
|
21075
22160
|
],
|
|
@@ -21115,7 +22200,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
21115
22200
|
for (const field of fieldsByKind[input.kind]) {
|
|
21116
22201
|
if (input[field] !== void 0) {
|
|
21117
22202
|
ctx.addIssue({
|
|
21118
|
-
code:
|
|
22203
|
+
code: z79.ZodIssueCode.custom,
|
|
21119
22204
|
path: [
|
|
21120
22205
|
field
|
|
21121
22206
|
],
|
|
@@ -21162,7 +22247,7 @@ function registerCreativeVariantTools(server, api) {
|
|
|
21162
22247
|
throw translateError(error);
|
|
21163
22248
|
}
|
|
21164
22249
|
});
|
|
21165
|
-
server.tool("create_creative_variant", createCreativeVariantGuidance.summary + " ES UNA ACCI\xD3N DE PAGO: primero llama estimate_creative_variant, muestra el rango de cr\xE9ditos al usuario y solo despu\xE9s invoca este tool con confirm=true. Las recomendaciones IA tambi\xE9n son acciones pagas a 2x del costo real. clientRequestId es obligatorio: genera un UUID por intenci\xF3n
|
|
22250
|
+
server.tool("create_creative_variant", createCreativeVariantGuidance.summary + " ES UNA ACCI\xD3N DE PAGO: primero llama estimate_creative_variant, muestra el rango de cr\xE9ditos al usuario y solo despu\xE9s invoca este tool con confirm=true. Las recomendaciones IA tambi\xE9n son acciones pagas a 2x del costo real. clientRequestId es obligatorio por contrato: genera un UUID por intenci\xF3n. El servidor deriva la identidad real del request desde los argumentos sem\xE1nticos, as\xED que un UUID nuevo NO desbloquea un intento distinto \u2014 si recibes status=error, pide confirmaci\xF3n al usuario y reinvoca con los mismos argumentos. Reutiliza el producto y referencias del task origen; crea un child task independiente y devuelve variantTaskId. Para landing puedes enviar instructions en texto libre y omitir sectionKey: el backend lo clasifica con IA. No expone ni permite cambiar videoModel, videoTier, videoStyle o narrativeStyle; esos valores se heredan del task origen.", CreateCreativeVariantInputBase.shape, CREATES_PAID, async (input) => {
|
|
21166
22251
|
try {
|
|
21167
22252
|
const validatedInput = parseVariantInput(CreateCreativeVariantInput, input);
|
|
21168
22253
|
const estimateInput = EstimateCreativeVariantInput.parse({
|
|
@@ -21189,8 +22274,16 @@ function registerCreativeVariantTools(server, api) {
|
|
|
21189
22274
|
});
|
|
21190
22275
|
sectionKey = LandingSectionKeyEnum.parse(recommendation.value);
|
|
21191
22276
|
}
|
|
22277
|
+
const derivedClientRequestId = derivePaidRequestKey({
|
|
22278
|
+
fingerprint: api.fingerprint(),
|
|
22279
|
+
tool: "create_creative_variant",
|
|
22280
|
+
input: {
|
|
22281
|
+
...validatedInput
|
|
22282
|
+
},
|
|
22283
|
+
nowMs: Date.now()
|
|
22284
|
+
});
|
|
21192
22285
|
const payload = {
|
|
21193
|
-
clientRequestId:
|
|
22286
|
+
clientRequestId: derivedClientRequestId,
|
|
21194
22287
|
kind: validatedInput.kind,
|
|
21195
22288
|
...validatedInput.kind !== "landing" ? {
|
|
21196
22289
|
angle: validatedInput.angle,
|
|
@@ -21216,7 +22309,7 @@ function registerCreativeVariantTools(server, api) {
|
|
|
21216
22309
|
};
|
|
21217
22310
|
const created = await api.postJson(`/v1/tasks/${encodeURIComponent(validatedInput.sourceTaskId)}/creative-variants`, payload);
|
|
21218
22311
|
if (created.status === "error") {
|
|
21219
|
-
throw mcp(McpErrorCode.CREATIVE_VARIANT_FAILED, "La variante anterior termin\xF3 con error. Pide confirmaci\xF3n para un intento nuevo
|
|
22312
|
+
throw mcp(McpErrorCode.CREATIVE_VARIANT_FAILED, "La variante anterior termin\xF3 con error. Pide confirmaci\xF3n al usuario y reinvoca con los mismos argumentos para un intento nuevo.", {
|
|
21220
22313
|
variantTaskId: created.variantTaskId
|
|
21221
22314
|
});
|
|
21222
22315
|
}
|
|
@@ -21232,32 +22325,32 @@ function registerCreativeVariantTools(server, api) {
|
|
|
21232
22325
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
21233
22326
|
|
|
21234
22327
|
// src/tools/script-review.ts
|
|
21235
|
-
import { z as
|
|
22328
|
+
import { z as z80 } from "zod";
|
|
21236
22329
|
var getVideoScriptGuidance = getProductMakerCapabilityModelGuidance("get_video_script");
|
|
21237
22330
|
var regenerateVideoScriptGuidance = getProductMakerCapabilityModelGuidance("regenerate_video_script");
|
|
21238
22331
|
var approveVideoScriptGuidance = getProductMakerCapabilityModelGuidance("approve_video_script");
|
|
21239
|
-
var GetVideoScriptInput =
|
|
21240
|
-
taskId:
|
|
21241
|
-
angleKey:
|
|
22332
|
+
var GetVideoScriptInput = z80.object({
|
|
22333
|
+
taskId: z80.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
22334
|
+
angleKey: z80.string().min(1).optional().describe('Clave del \xE1ngulo cuyo guion se quiere leer (formato "angle_0", "angle_1", ...). REQUERIDO para tareas multi-angle: si se omite, el backend resuelve a "angle_0" LITERAL (no "el primer pendiente"), as\xED que en multi-angle donde angle_0 ya est\xE9 aprobado/saltado obtendr\xEDas status="approved" y nunca alcanzar\xEDas el \xE1ngulo realmente pendiente. Para single-angle puede omitirse.')
|
|
21242
22335
|
}).strict();
|
|
21243
22336
|
var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
|
|
21244
22337
|
var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
|
|
21245
|
-
var ApproveSceneEdit =
|
|
21246
|
-
index:
|
|
21247
|
-
dialogue:
|
|
22338
|
+
var ApproveSceneEdit = z80.object({
|
|
22339
|
+
index: z80.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
|
|
22340
|
+
dialogue: z80.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
|
|
21248
22341
|
message: "dialogue no puede ser s\xF3lo espacios"
|
|
21249
22342
|
}).describe("Di\xE1logo final de la escena. NO puede estar vac\xEDo ni ser s\xF3lo espacios. Puede ser el original sin cambios o la edici\xF3n del usuario.")
|
|
21250
22343
|
}).strict();
|
|
21251
|
-
var ApproveVideoScriptInput =
|
|
21252
|
-
taskId:
|
|
21253
|
-
angleKey:
|
|
21254
|
-
scenes:
|
|
22344
|
+
var ApproveVideoScriptInput = z80.object({
|
|
22345
|
+
taskId: z80.string().min(1).describe("ID de la tarea."),
|
|
22346
|
+
angleKey: z80.string().trim().min(1).max(120).optional().describe('Mismo angleKey usado en get_video_script. REQUERIDO en multi-angle (si se omite el backend resuelve a "angle_0" literal). En single-angle puede omitirse.'),
|
|
22347
|
+
scenes: z80.array(ApproveSceneEdit).min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_SCENES2).describe("Escenas con los di\xE1logos finales. El backend mergea por `index` \u2014 las escenas NO incluidas conservan el di\xE1logo original. Debe haber entre 1 y 50 entradas. Para cada entrada que mandes con un dialogue distinto del original, el backend valida minWords \u2264 words \u2264 maxWords y rechaza con 400 si te sales del rango.")
|
|
21255
22348
|
}).strict();
|
|
21256
|
-
var RegenerateVideoScriptInput =
|
|
21257
|
-
taskId:
|
|
21258
|
-
angleKey:
|
|
22349
|
+
var RegenerateVideoScriptInput = z80.object({
|
|
22350
|
+
taskId: z80.string().min(1).describe("ID de la tarea."),
|
|
22351
|
+
angleKey: z80.string().min(1).describe('Clave del \xE1ngulo a regenerar. Single-angle: pasa "angle_0". Multi-angle: el mismo que devuelve get_video_script. NO se puede omitir \u2014 el backend lo exige siempre.'),
|
|
21259
22352
|
narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion. Incluye valores hist\xF3ricos porque esta herramienta tambi\xE9n regenera tareas antiguas. REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).'),
|
|
21260
|
-
narrativeSelection:
|
|
22353
|
+
narrativeSelection: z80.enum([
|
|
21261
22354
|
"market",
|
|
21262
22355
|
"guided"
|
|
21263
22356
|
]).optional().describe("Elecci\xF3n expl\xEDcita del usuario: market usa el estudio de mercado disponible; guided usa la narrativa elegida. Omitir conserva el comportamiento existente.")
|
|
@@ -21338,14 +22431,14 @@ function registerScriptReviewTools(server, api) {
|
|
|
21338
22431
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
21339
22432
|
|
|
21340
22433
|
// src/tools/connections.ts
|
|
21341
|
-
import { z as
|
|
22434
|
+
import { z as z81 } from "zod";
|
|
21342
22435
|
var listShopifyShopsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_shops");
|
|
21343
22436
|
var listShopifyProductsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_products");
|
|
21344
22437
|
var listMetaAdAccountsGuidance = getProductMakerCapabilityModelGuidance("list_meta_ad_accounts");
|
|
21345
22438
|
var listMetaDestinationsGuidance = getProductMakerCapabilityModelGuidance("list_meta_destinations");
|
|
21346
22439
|
var listTikTokAdvertisersGuidance = getProductMakerCapabilityModelGuidance("list_tiktok_advertisers");
|
|
21347
22440
|
function registerConnectionTools(server, api) {
|
|
21348
|
-
server.tool("list_shopify_shops", listShopifyShopsGuidance.summary + " Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.",
|
|
22441
|
+
server.tool("list_shopify_shops", listShopifyShopsGuidance.summary + " Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z81.object({}).shape, READS_INTERNAL, async () => {
|
|
21349
22442
|
try {
|
|
21350
22443
|
const r = await api.getJson("/v1/shopify/shops");
|
|
21351
22444
|
return asText(r);
|
|
@@ -21353,10 +22446,10 @@ function registerConnectionTools(server, api) {
|
|
|
21353
22446
|
throw translateError(e);
|
|
21354
22447
|
}
|
|
21355
22448
|
});
|
|
21356
|
-
server.tool("list_shopify_products", 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.',
|
|
21357
|
-
shopDomain:
|
|
21358
|
-
query:
|
|
21359
|
-
taskId:
|
|
22449
|
+
server.tool("list_shopify_products", 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.', z81.object({
|
|
22450
|
+
shopDomain: z81.string().min(3).max(120),
|
|
22451
|
+
query: z81.string().trim().max(120).optional(),
|
|
22452
|
+
taskId: z81.string().uuid().optional()
|
|
21360
22453
|
}).shape, READS_EXTERNAL, async ({ shopDomain, query, taskId }) => {
|
|
21361
22454
|
try {
|
|
21362
22455
|
const params = new URLSearchParams({
|
|
@@ -21370,7 +22463,7 @@ function registerConnectionTools(server, api) {
|
|
|
21370
22463
|
throw translateError(e);
|
|
21371
22464
|
}
|
|
21372
22465
|
});
|
|
21373
|
-
server.tool("list_meta_ad_accounts", 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.",
|
|
22466
|
+
server.tool("list_meta_ad_accounts", 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.", z81.object({}).shape, READS_EXTERNAL, async () => {
|
|
21374
22467
|
try {
|
|
21375
22468
|
const r = await api.getJson("/v1/facebook/ad-accounts");
|
|
21376
22469
|
return asText(r);
|
|
@@ -21380,7 +22473,7 @@ function registerConnectionTools(server, api) {
|
|
|
21380
22473
|
});
|
|
21381
22474
|
server.registerTool("list_meta_destinations", {
|
|
21382
22475
|
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.",
|
|
21383
|
-
inputSchema:
|
|
22476
|
+
inputSchema: z81.object({}).shape,
|
|
21384
22477
|
annotations: READS_EXTERNAL,
|
|
21385
22478
|
_meta: {
|
|
21386
22479
|
"openai/widgetAccessible": true
|
|
@@ -21393,7 +22486,7 @@ function registerConnectionTools(server, api) {
|
|
|
21393
22486
|
throw translateError(e);
|
|
21394
22487
|
}
|
|
21395
22488
|
});
|
|
21396
|
-
server.tool("list_tiktok_advertisers", listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.",
|
|
22489
|
+
server.tool("list_tiktok_advertisers", listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z81.object({}).shape, READS_EXTERNAL, async () => {
|
|
21397
22490
|
try {
|
|
21398
22491
|
const r = await api.getJson("/v1/tiktok/advertisers");
|
|
21399
22492
|
return asText(r);
|
|
@@ -21405,10 +22498,10 @@ function registerConnectionTools(server, api) {
|
|
|
21405
22498
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
21406
22499
|
|
|
21407
22500
|
// src/tools/actors.ts
|
|
21408
|
-
import { z as
|
|
22501
|
+
import { z as z82 } from "zod";
|
|
21409
22502
|
var listActorsGuidance = getProductMakerCapabilityModelGuidance("list_actors");
|
|
21410
22503
|
function registerActorTools(server, api) {
|
|
21411
|
-
server.tool("list_actors", listActorsGuidance.summary + " Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.",
|
|
22504
|
+
server.tool("list_actors", listActorsGuidance.summary + " Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.", z82.object({}).shape, READS_INTERNAL, async () => {
|
|
21412
22505
|
try {
|
|
21413
22506
|
const rows = await api.getJson("/v1/actors");
|
|
21414
22507
|
const actors = (Array.isArray(rows) ? rows : []).filter((actor) => actor.status === "ready").map(({ id, name, description, gender, voiceId }) => ({
|
|
@@ -21429,40 +22522,7 @@ function registerActorTools(server, api) {
|
|
|
21429
22522
|
__name(registerActorTools, "registerActorTools");
|
|
21430
22523
|
|
|
21431
22524
|
// src/tools/publish.ts
|
|
21432
|
-
import { z as
|
|
21433
|
-
|
|
21434
|
-
// src/tools/stable-uuid.ts
|
|
21435
|
-
import { createHash as createHash3 } from "crypto";
|
|
21436
|
-
function canonicalize(value) {
|
|
21437
|
-
if (Array.isArray(value)) return value.map(canonicalize);
|
|
21438
|
-
if (value === null || typeof value !== "object") return value;
|
|
21439
|
-
const record = value;
|
|
21440
|
-
return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
|
|
21441
|
-
key,
|
|
21442
|
-
canonicalize(record[key])
|
|
21443
|
-
]));
|
|
21444
|
-
}
|
|
21445
|
-
__name(canonicalize, "canonicalize");
|
|
21446
|
-
function canonicalJson(value) {
|
|
21447
|
-
return JSON.stringify(canonicalize(value));
|
|
21448
|
-
}
|
|
21449
|
-
__name(canonicalJson, "canonicalJson");
|
|
21450
|
-
function deriveUuidV5(name, namespace) {
|
|
21451
|
-
const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
|
|
21452
|
-
const digest = createHash3("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
21453
|
-
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
21454
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
21455
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
21456
|
-
const hex = bytes.toString("hex");
|
|
21457
|
-
return [
|
|
21458
|
-
hex.slice(0, 8),
|
|
21459
|
-
hex.slice(8, 12),
|
|
21460
|
-
hex.slice(12, 16),
|
|
21461
|
-
hex.slice(16, 20),
|
|
21462
|
-
hex.slice(20)
|
|
21463
|
-
].join("-");
|
|
21464
|
-
}
|
|
21465
|
-
__name(deriveUuidV5, "deriveUuidV5");
|
|
22525
|
+
import { z as z83 } from "zod";
|
|
21466
22526
|
|
|
21467
22527
|
// src/tools/meta-publish-attempt-id.ts
|
|
21468
22528
|
var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
|
|
@@ -21501,40 +22561,40 @@ var publishToShopifyGuidance = getProductMakerCapabilityModelGuidance("publish_t
|
|
|
21501
22561
|
var publishToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_to_meta");
|
|
21502
22562
|
var publishCreativesToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_creatives_to_meta");
|
|
21503
22563
|
var publishToTikTokGuidance = getProductMakerCapabilityModelGuidance("publish_to_tiktok");
|
|
21504
|
-
var ShopifyInput =
|
|
21505
|
-
taskId:
|
|
21506
|
-
shopDomain:
|
|
22564
|
+
var ShopifyInput = z83.object({
|
|
22565
|
+
taskId: z83.string().min(1),
|
|
22566
|
+
shopDomain: z83.string().min(1),
|
|
21507
22567
|
/** Override: number of landing images to include. Default = ALL images the
|
|
21508
22568
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
21509
22569
|
* generously here; the backend validates each index against
|
|
21510
22570
|
* landing.images.length and rejects out-of-range. */
|
|
21511
|
-
numLandingImages:
|
|
21512
|
-
variantIndex:
|
|
22571
|
+
numLandingImages: z83.number().int().min(1).max(20).optional(),
|
|
22572
|
+
variantIndex: z83.number().int().min(0).optional(),
|
|
21513
22573
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
21514
22574
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
21515
22575
|
* a single-option product page. Defaults to false. */
|
|
21516
|
-
showVariantPicker:
|
|
22576
|
+
showVariantPicker: z83.boolean().optional(),
|
|
21517
22577
|
/** Default `auto` updates the product previously published for this task and
|
|
21518
22578
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
21519
22579
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
21520
|
-
publicationMode:
|
|
22580
|
+
publicationMode: z83.enum([
|
|
21521
22581
|
"auto",
|
|
21522
22582
|
"existing",
|
|
21523
22583
|
"new"
|
|
21524
22584
|
]).optional(),
|
|
21525
22585
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
21526
22586
|
* publicationMode is `existing`. */
|
|
21527
|
-
targetProductId:
|
|
22587
|
+
targetProductId: z83.string().trim().min(1).max(300).optional(),
|
|
21528
22588
|
/** Add an announcement bar text above add-to-cart (optional). */
|
|
21529
|
-
announcementBarEnabled:
|
|
21530
|
-
announcementBarText:
|
|
22589
|
+
announcementBarEnabled: z83.boolean().optional(),
|
|
22590
|
+
announcementBarText: z83.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
|
|
21531
22591
|
commerceCorrection: PublishCommerceCorrectionSchema.optional().describe("Complete seller-confirmed commerce values, with the revision and fingerprint returned in recovery. Never invent missing prices or convert currency."),
|
|
21532
|
-
confirmed:
|
|
22592
|
+
confirmed: z83.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
|
|
21533
22593
|
});
|
|
21534
22594
|
var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value, ctx) => {
|
|
21535
22595
|
if (value.commerceCorrection && value.confirmed !== true) {
|
|
21536
22596
|
ctx.addIssue({
|
|
21537
|
-
code:
|
|
22597
|
+
code: z83.ZodIssueCode.custom,
|
|
21538
22598
|
path: [
|
|
21539
22599
|
"confirmed"
|
|
21540
22600
|
],
|
|
@@ -21543,7 +22603,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
21543
22603
|
}
|
|
21544
22604
|
if (value.announcementBarEnabled && !value.announcementBarText) {
|
|
21545
22605
|
ctx.addIssue({
|
|
21546
|
-
code:
|
|
22606
|
+
code: z83.ZodIssueCode.custom,
|
|
21547
22607
|
path: [
|
|
21548
22608
|
"announcementBarText"
|
|
21549
22609
|
],
|
|
@@ -21552,7 +22612,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
21552
22612
|
}
|
|
21553
22613
|
if (!value.announcementBarEnabled && value.announcementBarText) {
|
|
21554
22614
|
ctx.addIssue({
|
|
21555
|
-
code:
|
|
22615
|
+
code: z83.ZodIssueCode.custom,
|
|
21556
22616
|
path: [
|
|
21557
22617
|
"announcementBarText"
|
|
21558
22618
|
],
|
|
@@ -21566,91 +22626,91 @@ function parseShopifyInput(rawInput) {
|
|
|
21566
22626
|
return parsed.data;
|
|
21567
22627
|
}
|
|
21568
22628
|
__name(parseShopifyInput, "parseShopifyInput");
|
|
21569
|
-
var McpCampaignConfig =
|
|
22629
|
+
var McpCampaignConfig = z83.object({
|
|
21570
22630
|
/** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
|
|
21571
22631
|
* converts to the account currency server-side. Do NOT pre-convert: on a COP
|
|
21572
22632
|
* account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
|
|
21573
|
-
dailyBudgetUsd:
|
|
22633
|
+
dailyBudgetUsd: z83.number().positive().optional(),
|
|
21574
22634
|
/** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
|
|
21575
22635
|
* normalizes at validation). Meta accepts max 1 (single-country ad set).
|
|
21576
22636
|
* TikTok takes only the first entry. */
|
|
21577
|
-
targetCountries:
|
|
21578
|
-
campaignName:
|
|
22637
|
+
targetCountries: z83.array(CountryIso2Schema).max(1).optional(),
|
|
22638
|
+
campaignName: z83.string().trim().min(1).max(100).optional()
|
|
21579
22639
|
});
|
|
21580
22640
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
21581
|
-
var MetaInput =
|
|
21582
|
-
taskId:
|
|
22641
|
+
var MetaInput = z83.object({
|
|
22642
|
+
taskId: z83.string().min(1),
|
|
21583
22643
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
21584
|
-
portfolioId:
|
|
22644
|
+
portfolioId: z83.string().trim().min(1),
|
|
21585
22645
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
21586
|
-
pageId:
|
|
22646
|
+
pageId: z83.string().trim().min(1),
|
|
21587
22647
|
adAccountId: McpAdAccountId,
|
|
21588
|
-
pixelId:
|
|
21589
|
-
landingUrl:
|
|
22648
|
+
pixelId: z83.string().min(1).optional(),
|
|
22649
|
+
landingUrl: z83.string().url().optional(),
|
|
21590
22650
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
21591
22651
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
21592
|
-
angleIndexes:
|
|
22652
|
+
angleIndexes: z83.array(z83.number().int().nonnegative()).optional(),
|
|
21593
22653
|
campaignConfig: McpCampaignConfig.optional(),
|
|
21594
22654
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
21595
22655
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
21596
22656
|
* (Meta's primary-text limit). */
|
|
21597
|
-
primaryText:
|
|
22657
|
+
primaryText: z83.string().trim().min(1).max(500).optional(),
|
|
21598
22658
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
21599
22659
|
* (wa.me) instead of a landing page. */
|
|
21600
|
-
phoneNumber:
|
|
22660
|
+
phoneNumber: z83.string().min(8).max(32).optional()
|
|
21601
22661
|
});
|
|
21602
|
-
var GroupedTaskSource =
|
|
21603
|
-
type:
|
|
21604
|
-
taskId:
|
|
21605
|
-
angleIndex:
|
|
21606
|
-
videoVariant:
|
|
22662
|
+
var GroupedTaskSource = z83.object({
|
|
22663
|
+
type: z83.literal("task"),
|
|
22664
|
+
taskId: z83.string().min(1),
|
|
22665
|
+
angleIndex: z83.number().int().min(0).optional(),
|
|
22666
|
+
videoVariant: z83.enum([
|
|
21607
22667
|
"with_subtitles",
|
|
21608
22668
|
"without_subtitles"
|
|
21609
22669
|
]).optional(),
|
|
21610
|
-
imageVariantIndex:
|
|
21611
|
-
});
|
|
21612
|
-
var GroupedMediaSource =
|
|
21613
|
-
type:
|
|
21614
|
-
url:
|
|
21615
|
-
path:
|
|
21616
|
-
thumbnailUrl:
|
|
21617
|
-
thumbnailPath:
|
|
22670
|
+
imageVariantIndex: z83.number().int().min(0).optional()
|
|
22671
|
+
});
|
|
22672
|
+
var GroupedMediaSource = z83.object({
|
|
22673
|
+
type: z83.literal("media"),
|
|
22674
|
+
url: z83.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
|
|
22675
|
+
path: z83.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".'),
|
|
22676
|
+
thumbnailUrl: z83.string().url().optional(),
|
|
22677
|
+
thumbnailPath: z83.string().min(1).optional()
|
|
21618
22678
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
21619
22679
|
message: "media source needs exactly one of `url` or `path`"
|
|
21620
22680
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
21621
22681
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
21622
22682
|
});
|
|
21623
|
-
var GroupedUploadedAssetSource =
|
|
21624
|
-
type:
|
|
22683
|
+
var GroupedUploadedAssetSource = z83.object({
|
|
22684
|
+
type: z83.literal("asset"),
|
|
21625
22685
|
/** Returned by check_media_upload. ProductMaker resolves ownership server-side. */
|
|
21626
|
-
assetId:
|
|
21627
|
-
thumbnailAssetId:
|
|
22686
|
+
assetId: z83.string().uuid(),
|
|
22687
|
+
thumbnailAssetId: z83.string().uuid().optional()
|
|
21628
22688
|
});
|
|
21629
|
-
var GroupedCreative =
|
|
21630
|
-
type:
|
|
22689
|
+
var GroupedCreative = z83.object({
|
|
22690
|
+
type: z83.enum([
|
|
21631
22691
|
"image",
|
|
21632
22692
|
"video"
|
|
21633
22693
|
]),
|
|
21634
|
-
source:
|
|
22694
|
+
source: z83.union([
|
|
21635
22695
|
GroupedTaskSource,
|
|
21636
22696
|
GroupedMediaSource,
|
|
21637
22697
|
GroupedUploadedAssetSource
|
|
21638
22698
|
]),
|
|
21639
|
-
title:
|
|
21640
|
-
description:
|
|
22699
|
+
title: z83.string().max(120).optional(),
|
|
22700
|
+
description: z83.string().max(500).optional(),
|
|
21641
22701
|
cta: MetaCtaType.optional(),
|
|
21642
|
-
creativeId:
|
|
22702
|
+
creativeId: z83.string().min(1).optional()
|
|
21643
22703
|
});
|
|
21644
|
-
var GroupedCampaignConfigBase =
|
|
22704
|
+
var GroupedCampaignConfigBase = z83.object({
|
|
21645
22705
|
campaignType: MetaCampaignType.default("sales"),
|
|
21646
22706
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
21647
22707
|
budgetMode: MetaBudgetMode.optional(),
|
|
21648
|
-
campaignDailyBudget:
|
|
21649
|
-
adSets:
|
|
21650
|
-
tempId:
|
|
21651
|
-
name:
|
|
21652
|
-
dailyBudget:
|
|
21653
|
-
creativeIds:
|
|
22708
|
+
campaignDailyBudget: z83.number().int().positive().optional(),
|
|
22709
|
+
adSets: z83.array(z83.object({
|
|
22710
|
+
tempId: z83.string().min(1).max(80),
|
|
22711
|
+
name: z83.string().min(1).max(100),
|
|
22712
|
+
dailyBudget: z83.number().int().positive().optional(),
|
|
22713
|
+
creativeIds: z83.array(z83.string().min(1)).min(1)
|
|
21654
22714
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
21655
22715
|
/**
|
|
21656
22716
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -21658,17 +22718,17 @@ var GroupedCampaignConfigBase = z79.object({
|
|
|
21658
22718
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
21659
22719
|
* the API since #849 — unreachable from this tool.
|
|
21660
22720
|
*/
|
|
21661
|
-
destination:
|
|
21662
|
-
|
|
21663
|
-
type:
|
|
21664
|
-
landingUrl:
|
|
22721
|
+
destination: z83.discriminatedUnion("type", [
|
|
22722
|
+
z83.object({
|
|
22723
|
+
type: z83.literal("landing"),
|
|
22724
|
+
landingUrl: z83.string().url()
|
|
21665
22725
|
}),
|
|
21666
|
-
|
|
21667
|
-
type:
|
|
21668
|
-
phoneNumber:
|
|
22726
|
+
z83.object({
|
|
22727
|
+
type: z83.literal("whatsapp"),
|
|
22728
|
+
phoneNumber: z83.string().min(8).max(32)
|
|
21669
22729
|
})
|
|
21670
22730
|
]),
|
|
21671
|
-
saveAsDefault:
|
|
22731
|
+
saveAsDefault: z83.boolean().default(false),
|
|
21672
22732
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
21673
22733
|
});
|
|
21674
22734
|
function resolveBudgetMode(value) {
|
|
@@ -21682,7 +22742,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
21682
22742
|
const budgetMode = resolveBudgetMode(value);
|
|
21683
22743
|
if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
21684
22744
|
ctx.addIssue({
|
|
21685
|
-
code:
|
|
22745
|
+
code: z83.ZodIssueCode.custom,
|
|
21686
22746
|
path: [
|
|
21687
22747
|
"campaignDailyBudget"
|
|
21688
22748
|
],
|
|
@@ -21693,7 +22753,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
21693
22753
|
value.adSets.forEach((adSet, index) => {
|
|
21694
22754
|
if (adSet.dailyBudget === void 0) {
|
|
21695
22755
|
ctx.addIssue({
|
|
21696
|
-
code:
|
|
22756
|
+
code: z83.ZodIssueCode.custom,
|
|
21697
22757
|
path: [
|
|
21698
22758
|
"adSets",
|
|
21699
22759
|
index,
|
|
@@ -21708,28 +22768,28 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
21708
22768
|
...value,
|
|
21709
22769
|
budgetMode: resolveBudgetMode(value)
|
|
21710
22770
|
}));
|
|
21711
|
-
var PublishCreativesToMetaInputBase =
|
|
21712
|
-
creatives:
|
|
22771
|
+
var PublishCreativesToMetaInputBase = z83.object({
|
|
22772
|
+
creatives: z83.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
21713
22773
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
21714
|
-
portfolioId:
|
|
22774
|
+
portfolioId: z83.string().trim().min(1),
|
|
21715
22775
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
21716
|
-
pageId:
|
|
22776
|
+
pageId: z83.string().trim().min(1),
|
|
21717
22777
|
adAccountId: McpAdAccountId,
|
|
21718
|
-
pixelId:
|
|
22778
|
+
pixelId: z83.string().min(1).optional(),
|
|
21719
22779
|
/** Required unless `campaignConfig.destination` is whatsapp — see the
|
|
21720
22780
|
* superRefine below. Mirrors the API's `FacebookCreateCampaignFromTasksBodySchema`. */
|
|
21721
|
-
landingUrl:
|
|
22781
|
+
landingUrl: z83.string().url().optional(),
|
|
21722
22782
|
/** ISO-2 code or resolvable country name — mirrors the API contract
|
|
21723
22783
|
* (CountryIso2Schema), which normalizes names to alpha-2 at validation. */
|
|
21724
22784
|
targetCountry: CountryIso2Schema,
|
|
21725
|
-
campaignName:
|
|
22785
|
+
campaignName: z83.string().trim().min(1).max(100),
|
|
21726
22786
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
21727
22787
|
});
|
|
21728
22788
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
21729
22789
|
const destination = value.campaignConfig?.destination;
|
|
21730
22790
|
if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl) {
|
|
21731
22791
|
ctx.addIssue({
|
|
21732
|
-
code:
|
|
22792
|
+
code: z83.ZodIssueCode.custom,
|
|
21733
22793
|
path: [
|
|
21734
22794
|
"landingUrl"
|
|
21735
22795
|
],
|
|
@@ -21738,7 +22798,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
21738
22798
|
}
|
|
21739
22799
|
if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
|
|
21740
22800
|
ctx.addIssue({
|
|
21741
|
-
code:
|
|
22801
|
+
code: z83.ZodIssueCode.custom,
|
|
21742
22802
|
path: [
|
|
21743
22803
|
"campaignConfig",
|
|
21744
22804
|
"destination",
|
|
@@ -21757,16 +22817,16 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
21757
22817
|
}
|
|
21758
22818
|
return value;
|
|
21759
22819
|
});
|
|
21760
|
-
var PresignUploadResponseSchema =
|
|
21761
|
-
uploadUrl:
|
|
22820
|
+
var PresignUploadResponseSchema = z83.object({
|
|
22821
|
+
uploadUrl: z83.string().url().refine((value) => {
|
|
21762
22822
|
const url = new URL(value);
|
|
21763
22823
|
return !url.username && !url.password && url.protocol === "https:";
|
|
21764
22824
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
21765
|
-
publicUrl:
|
|
22825
|
+
publicUrl: z83.string().url().refine((value) => {
|
|
21766
22826
|
const url = new URL(value);
|
|
21767
22827
|
return !url.username && !url.password;
|
|
21768
22828
|
}, "public media URL must not contain embedded credentials"),
|
|
21769
|
-
stagingAssetId:
|
|
22829
|
+
stagingAssetId: z83.string().uuid()
|
|
21770
22830
|
});
|
|
21771
22831
|
async function uploadResolvedMedia(api, media, assetRole) {
|
|
21772
22832
|
if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
|
|
@@ -21791,16 +22851,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
|
|
|
21791
22851
|
return uploadResolvedMedia(api, media, assetRole);
|
|
21792
22852
|
}
|
|
21793
22853
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
21794
|
-
var TikTokInput =
|
|
21795
|
-
taskId:
|
|
21796
|
-
advertiserId:
|
|
21797
|
-
pixelId:
|
|
21798
|
-
landingUrl:
|
|
21799
|
-
angleIndexes:
|
|
22854
|
+
var TikTokInput = z83.object({
|
|
22855
|
+
taskId: z83.string().min(1),
|
|
22856
|
+
advertiserId: z83.string().min(1),
|
|
22857
|
+
pixelId: z83.string().min(1).optional(),
|
|
22858
|
+
landingUrl: z83.string().url().optional(),
|
|
22859
|
+
angleIndexes: z83.array(z83.number().int().nonnegative()).optional(),
|
|
21800
22860
|
campaignConfig: McpCampaignConfig.optional(),
|
|
21801
22861
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
21802
22862
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
21803
|
-
primaryText:
|
|
22863
|
+
primaryText: z83.string().trim().min(1).max(500).optional()
|
|
21804
22864
|
});
|
|
21805
22865
|
function registerPublishTools(server, api, opts = {}) {
|
|
21806
22866
|
const transport = opts.transport ?? "http";
|
|
@@ -22128,39 +23188,39 @@ __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
|
22128
23188
|
|
|
22129
23189
|
// src/tools/meta-upload.ts
|
|
22130
23190
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
22131
|
-
import { z as
|
|
23191
|
+
import { z as z84 } from "zod";
|
|
22132
23192
|
var requestMediaUploadGuidance = getProductMakerCapabilityModelGuidance("request_media_upload");
|
|
22133
|
-
var UploadSessionInputBase =
|
|
22134
|
-
mediaKind:
|
|
23193
|
+
var UploadSessionInputBase = z84.object({
|
|
23194
|
+
mediaKind: z84.enum([
|
|
22135
23195
|
"image",
|
|
22136
23196
|
"video",
|
|
22137
23197
|
"mixed"
|
|
22138
23198
|
]).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."),
|
|
22139
|
-
assetRole:
|
|
23199
|
+
assetRole: z84.enum([
|
|
22140
23200
|
"media",
|
|
22141
23201
|
"thumbnail"
|
|
22142
23202
|
]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail."),
|
|
22143
|
-
uploadPurpose:
|
|
23203
|
+
uploadPurpose: z84.enum([
|
|
22144
23204
|
"product_reference",
|
|
22145
23205
|
"creative",
|
|
22146
23206
|
"generic"
|
|
22147
23207
|
]).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."),
|
|
22148
|
-
maxFiles:
|
|
23208
|
+
maxFiles: z84.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
|
|
22149
23209
|
});
|
|
22150
|
-
var OpenAiFileReference =
|
|
22151
|
-
download_url:
|
|
22152
|
-
file_id:
|
|
22153
|
-
mime_type:
|
|
22154
|
-
file_name:
|
|
23210
|
+
var OpenAiFileReference = z84.object({
|
|
23211
|
+
download_url: z84.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."),
|
|
23212
|
+
file_id: z84.string().min(1),
|
|
23213
|
+
mime_type: z84.string().min(1).optional(),
|
|
23214
|
+
file_name: z84.string().min(1).optional()
|
|
22155
23215
|
});
|
|
22156
23216
|
var UploadSessionToolInputShape = {
|
|
22157
23217
|
...UploadSessionInputBase.shape,
|
|
22158
|
-
files:
|
|
23218
|
+
files: z84.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.")
|
|
22159
23219
|
};
|
|
22160
|
-
var UploadSessionToolInput =
|
|
23220
|
+
var UploadSessionToolInput = z84.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
|
|
22161
23221
|
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
22162
23222
|
ctx.addIssue({
|
|
22163
|
-
code:
|
|
23223
|
+
code: z84.ZodIssueCode.custom,
|
|
22164
23224
|
path: [
|
|
22165
23225
|
"mediaKind"
|
|
22166
23226
|
],
|
|
@@ -22169,7 +23229,7 @@ var UploadSessionToolInput = z80.object(UploadSessionToolInputShape).superRefine
|
|
|
22169
23229
|
}
|
|
22170
23230
|
if (value.mediaKind === "video" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
|
|
22171
23231
|
ctx.addIssue({
|
|
22172
|
-
code:
|
|
23232
|
+
code: z84.ZodIssueCode.custom,
|
|
22173
23233
|
path: [
|
|
22174
23234
|
"maxFiles"
|
|
22175
23235
|
],
|
|
@@ -22178,7 +23238,7 @@ var UploadSessionToolInput = z80.object(UploadSessionToolInputShape).superRefine
|
|
|
22178
23238
|
}
|
|
22179
23239
|
if (value.mediaKind === "image" && (value.maxFiles ?? 1) > 5) {
|
|
22180
23240
|
ctx.addIssue({
|
|
22181
|
-
code:
|
|
23241
|
+
code: z84.ZodIssueCode.custom,
|
|
22182
23242
|
path: [
|
|
22183
23243
|
"maxFiles"
|
|
22184
23244
|
],
|
|
@@ -22186,38 +23246,38 @@ var UploadSessionToolInput = z80.object(UploadSessionToolInputShape).superRefine
|
|
|
22186
23246
|
});
|
|
22187
23247
|
}
|
|
22188
23248
|
});
|
|
22189
|
-
var UploadSessionResponse =
|
|
22190
|
-
sessionId:
|
|
22191
|
-
uploadPageUrl:
|
|
22192
|
-
mediaKind:
|
|
23249
|
+
var UploadSessionResponse = z84.object({
|
|
23250
|
+
sessionId: z84.string().uuid(),
|
|
23251
|
+
uploadPageUrl: z84.string().url(),
|
|
23252
|
+
mediaKind: z84.enum([
|
|
22193
23253
|
"image",
|
|
22194
23254
|
"video",
|
|
22195
23255
|
"mixed"
|
|
22196
23256
|
]),
|
|
22197
|
-
assetRole:
|
|
23257
|
+
assetRole: z84.enum([
|
|
22198
23258
|
"media",
|
|
22199
23259
|
"thumbnail"
|
|
22200
23260
|
]),
|
|
22201
|
-
maxFiles:
|
|
22202
|
-
expiresAt:
|
|
23261
|
+
maxFiles: z84.number().int().positive(),
|
|
23262
|
+
expiresAt: z84.string().datetime()
|
|
22203
23263
|
});
|
|
22204
|
-
var UploadStatusInput =
|
|
22205
|
-
sessionId:
|
|
23264
|
+
var UploadStatusInput = z84.object({
|
|
23265
|
+
sessionId: z84.string().uuid().describe("The sessionId returned by request_media_upload.")
|
|
22206
23266
|
});
|
|
22207
|
-
var UniversalUploadStatusResponse =
|
|
22208
|
-
|
|
22209
|
-
status:
|
|
22210
|
-
assets:
|
|
23267
|
+
var UniversalUploadStatusResponse = z84.discriminatedUnion("status", [
|
|
23268
|
+
z84.object({
|
|
23269
|
+
status: z84.literal("pending"),
|
|
23270
|
+
assets: z84.array(z84.never()).length(0)
|
|
22211
23271
|
}),
|
|
22212
|
-
|
|
22213
|
-
status:
|
|
22214
|
-
assets:
|
|
22215
|
-
assetId:
|
|
22216
|
-
mediaKind:
|
|
23272
|
+
z84.object({
|
|
23273
|
+
status: z84.literal("uploaded"),
|
|
23274
|
+
assets: z84.array(z84.object({
|
|
23275
|
+
assetId: z84.string().uuid(),
|
|
23276
|
+
mediaKind: z84.enum([
|
|
22217
23277
|
"image",
|
|
22218
23278
|
"video"
|
|
22219
23279
|
]),
|
|
22220
|
-
assetRole:
|
|
23280
|
+
assetRole: z84.enum([
|
|
22221
23281
|
"media",
|
|
22222
23282
|
"thumbnail"
|
|
22223
23283
|
])
|
|
@@ -22225,13 +23285,13 @@ var UniversalUploadStatusResponse = z80.discriminatedUnion("status", [
|
|
|
22225
23285
|
})
|
|
22226
23286
|
]);
|
|
22227
23287
|
var UniversalUploadStructuredResponse = UploadSessionResponse.extend({
|
|
22228
|
-
uploadPurpose:
|
|
23288
|
+
uploadPurpose: z84.enum([
|
|
22229
23289
|
"product_reference",
|
|
22230
23290
|
"creative",
|
|
22231
23291
|
"generic"
|
|
22232
23292
|
]),
|
|
22233
|
-
checkToolName:
|
|
22234
|
-
resolveEndpoint:
|
|
23293
|
+
checkToolName: z84.literal("check_media_upload"),
|
|
23294
|
+
resolveEndpoint: z84.string().url()
|
|
22235
23295
|
});
|
|
22236
23296
|
function contextualizeUploadPageUrl(uploadPageUrl, uploadPurpose) {
|
|
22237
23297
|
const url = new URL(uploadPageUrl);
|
|
@@ -22322,15 +23382,15 @@ function registerMediaUploadTools(server, api) {
|
|
|
22322
23382
|
__name(registerMediaUploadTools, "registerMediaUploadTools");
|
|
22323
23383
|
|
|
22324
23384
|
// src/tools/campaigns.ts
|
|
22325
|
-
import { z as
|
|
23385
|
+
import { z as z85 } from "zod";
|
|
22326
23386
|
var getMetaCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_meta_campaign_status");
|
|
22327
23387
|
var getTikTokCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_tiktok_campaign_status");
|
|
22328
|
-
var MetaStatusInput =
|
|
22329
|
-
campaignId:
|
|
23388
|
+
var MetaStatusInput = z85.object({
|
|
23389
|
+
campaignId: z85.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
22330
23390
|
});
|
|
22331
|
-
var TikTokStatusInput =
|
|
22332
|
-
campaignId:
|
|
22333
|
-
advertiserId:
|
|
23391
|
+
var TikTokStatusInput = z85.object({
|
|
23392
|
+
campaignId: z85.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
23393
|
+
advertiserId: z85.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
22334
23394
|
});
|
|
22335
23395
|
function registerCampaignTools(server, api) {
|
|
22336
23396
|
server.tool("get_meta_campaign_status", getMetaCampaignStatusGuidance.summary + " Llama esto DESPU\xC9S de `publish_to_meta` para confirmar que la campa\xF1a se cre\xF3 correctamente y para reportar performance al usuario. Es read-only \u2014 no modifica nada en Meta Ads Manager.", MetaStatusInput.shape, READS_EXTERNAL, async (input) => {
|
|
@@ -22427,10 +23487,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
22427
23487
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
22428
23488
|
|
|
22429
23489
|
// ../../packages/product-economics/src/product-costing.ts
|
|
22430
|
-
import { z as
|
|
23490
|
+
import { z as z87 } from "zod";
|
|
22431
23491
|
|
|
22432
23492
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
22433
|
-
import { z as
|
|
23493
|
+
import { z as z86 } from "zod";
|
|
22434
23494
|
|
|
22435
23495
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
22436
23496
|
var product_costing_shipping_references_default = {
|
|
@@ -22494,21 +23554,21 @@ var product_costing_shipping_references_default = {
|
|
|
22494
23554
|
};
|
|
22495
23555
|
|
|
22496
23556
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
22497
|
-
var ShippingReferenceSchema =
|
|
22498
|
-
reference:
|
|
22499
|
-
min:
|
|
22500
|
-
max:
|
|
22501
|
-
updatedAt:
|
|
22502
|
-
confidence:
|
|
23557
|
+
var ShippingReferenceSchema = z86.object({
|
|
23558
|
+
reference: z86.number().positive(),
|
|
23559
|
+
min: z86.number().nonnegative(),
|
|
23560
|
+
max: z86.number().positive(),
|
|
23561
|
+
updatedAt: z86.string().date(),
|
|
23562
|
+
confidence: z86.enum([
|
|
22503
23563
|
"high",
|
|
22504
23564
|
"medium",
|
|
22505
23565
|
"medium-low"
|
|
22506
23566
|
]),
|
|
22507
|
-
sources:
|
|
23567
|
+
sources: z86.array(z86.string().url().startsWith("https://")).min(1).max(3)
|
|
22508
23568
|
}).superRefine(({ reference, min, max }, context) => {
|
|
22509
23569
|
if (min > reference || reference > max) {
|
|
22510
23570
|
context.addIssue({
|
|
22511
|
-
code:
|
|
23571
|
+
code: z86.ZodIssueCode.custom,
|
|
22512
23572
|
message: "shipping_reference_outside_range",
|
|
22513
23573
|
path: [
|
|
22514
23574
|
"reference"
|
|
@@ -22516,7 +23576,7 @@ var ShippingReferenceSchema = z82.object({
|
|
|
22516
23576
|
});
|
|
22517
23577
|
}
|
|
22518
23578
|
});
|
|
22519
|
-
var SHIPPING_REFERENCES =
|
|
23579
|
+
var SHIPPING_REFERENCES = z86.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
22520
23580
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
22521
23581
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
22522
23582
|
"MX",
|
|
@@ -22698,18 +23758,18 @@ function getProductCostingCountryName(country) {
|
|
|
22698
23758
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
22699
23759
|
|
|
22700
23760
|
// ../../packages/product-economics/src/product-costing.ts
|
|
22701
|
-
var ProductCostingInputSchema =
|
|
22702
|
-
country:
|
|
22703
|
-
supplierCost:
|
|
22704
|
-
shippingCost:
|
|
22705
|
-
returnRate:
|
|
22706
|
-
cancellationRate:
|
|
22707
|
-
adsManagerCpa:
|
|
22708
|
-
desiredMargin:
|
|
23761
|
+
var ProductCostingInputSchema = z87.object({
|
|
23762
|
+
country: z87.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
23763
|
+
supplierCost: z87.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
23764
|
+
shippingCost: z87.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
23765
|
+
returnRate: z87.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
23766
|
+
cancellationRate: z87.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
23767
|
+
adsManagerCpa: z87.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
23768
|
+
desiredMargin: z87.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
22709
23769
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
22710
23770
|
if (returnRate + cancellationRate >= 1) {
|
|
22711
23771
|
context.addIssue({
|
|
22712
|
-
code:
|
|
23772
|
+
code: z87.ZodIssueCode.custom,
|
|
22713
23773
|
message: "delivery_rate_positive",
|
|
22714
23774
|
path: [
|
|
22715
23775
|
"cancellationRate"
|
|
@@ -22806,15 +23866,15 @@ function calculateProductCosting(rawInput) {
|
|
|
22806
23866
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
22807
23867
|
|
|
22808
23868
|
// src/tools/product-costing.ts
|
|
22809
|
-
import { z as
|
|
23869
|
+
import { z as z88 } from "zod";
|
|
22810
23870
|
var costProductGuidance = getProductMakerCapabilityModelGuidance("cost_product");
|
|
22811
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
22812
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
22813
|
-
var ProductCostingToolInput =
|
|
22814
|
-
country:
|
|
23871
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z88.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
23872
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z88.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
23873
|
+
var ProductCostingToolInput = z88.object({
|
|
23874
|
+
country: z88.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
22815
23875
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
22816
23876
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
22817
|
-
supplierCost:
|
|
23877
|
+
supplierCost: z88.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."),
|
|
22818
23878
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
22819
23879
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
22820
23880
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -22973,7 +24033,7 @@ function formatPercent(value) {
|
|
|
22973
24033
|
__name(formatPercent, "formatPercent");
|
|
22974
24034
|
|
|
22975
24035
|
// src/tools/product-research.ts
|
|
22976
|
-
import { z as
|
|
24036
|
+
import { z as z89 } from "zod";
|
|
22977
24037
|
|
|
22978
24038
|
// src/tools/product-research-idempotency.ts
|
|
22979
24039
|
var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
|
|
@@ -23344,35 +24404,35 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
23344
24404
|
]);
|
|
23345
24405
|
var POLL_INTERVAL_MS = 5e3;
|
|
23346
24406
|
var MAX_WAIT_SECONDS = 120;
|
|
23347
|
-
var FindInput =
|
|
24407
|
+
var FindInput = z89.object({
|
|
23348
24408
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
23349
24409
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
23350
|
-
confirmed:
|
|
23351
|
-
waitSeconds:
|
|
24410
|
+
confirmed: z89.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
|
|
24411
|
+
waitSeconds: z89.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
|
|
23352
24412
|
});
|
|
23353
|
-
var GetInput =
|
|
23354
|
-
runId:
|
|
24413
|
+
var GetInput = z89.object({
|
|
24414
|
+
runId: z89.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
23355
24415
|
});
|
|
23356
|
-
var ListInput =
|
|
23357
|
-
limit:
|
|
24416
|
+
var ListInput = z89.object({
|
|
24417
|
+
limit: z89.number().int().min(1).max(20).default(10)
|
|
23358
24418
|
});
|
|
23359
|
-
function textResult(
|
|
24419
|
+
function textResult(text2) {
|
|
23360
24420
|
return {
|
|
23361
24421
|
content: [
|
|
23362
24422
|
{
|
|
23363
24423
|
type: "text",
|
|
23364
|
-
text
|
|
24424
|
+
text: text2
|
|
23365
24425
|
}
|
|
23366
24426
|
]
|
|
23367
24427
|
};
|
|
23368
24428
|
}
|
|
23369
24429
|
__name(textResult, "textResult");
|
|
23370
|
-
function researchResult(run,
|
|
24430
|
+
function researchResult(run, text2) {
|
|
23371
24431
|
return {
|
|
23372
24432
|
content: [
|
|
23373
24433
|
{
|
|
23374
24434
|
type: "text",
|
|
23375
|
-
text
|
|
24435
|
+
text: text2
|
|
23376
24436
|
}
|
|
23377
24437
|
],
|
|
23378
24438
|
structuredContent: run
|
|
@@ -23477,17 +24537,17 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
23477
24537
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
23478
24538
|
|
|
23479
24539
|
// src/tools/dropshipping-projects.ts
|
|
23480
|
-
import { z as
|
|
24540
|
+
import { z as z90 } from "zod";
|
|
23481
24541
|
var startDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("start_dropshipping_project");
|
|
23482
24542
|
var costDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("cost_dropshipping_project");
|
|
23483
24543
|
var guideDropshippingLaunchGuidance = getProductMakerCapabilityModelGuidance("guide_dropshipping_launch");
|
|
23484
|
-
var ProjectId =
|
|
23485
|
-
var DropshippingProjectStatus =
|
|
24544
|
+
var ProjectId = z90.string().uuid();
|
|
24545
|
+
var DropshippingProjectStatus = z90.enum([
|
|
23486
24546
|
"active",
|
|
23487
24547
|
"completed",
|
|
23488
24548
|
"archived"
|
|
23489
24549
|
]);
|
|
23490
|
-
var DropshippingProjectPhase =
|
|
24550
|
+
var DropshippingProjectPhase = z90.enum([
|
|
23491
24551
|
"research",
|
|
23492
24552
|
"costing",
|
|
23493
24553
|
"media",
|
|
@@ -23496,21 +24556,21 @@ var DropshippingProjectPhase = z86.enum([
|
|
|
23496
24556
|
"advertising",
|
|
23497
24557
|
"complete"
|
|
23498
24558
|
]);
|
|
23499
|
-
var DropshippingRewardMilestone =
|
|
24559
|
+
var DropshippingRewardMilestone = z90.enum([
|
|
23500
24560
|
"product_selected",
|
|
23501
24561
|
"costing_saved",
|
|
23502
24562
|
"product_media_ready",
|
|
23503
24563
|
"creatives_completed",
|
|
23504
24564
|
"campaign_created"
|
|
23505
24565
|
]);
|
|
23506
|
-
var TargetCountry =
|
|
24566
|
+
var TargetCountry = z90.enum([
|
|
23507
24567
|
"CO",
|
|
23508
24568
|
"MX",
|
|
23509
24569
|
"PE",
|
|
23510
24570
|
"EC",
|
|
23511
24571
|
"CL"
|
|
23512
24572
|
]);
|
|
23513
|
-
var ResearchCategory =
|
|
24573
|
+
var ResearchCategory = z90.enum([
|
|
23514
24574
|
"auto",
|
|
23515
24575
|
"automotive",
|
|
23516
24576
|
"kitchen",
|
|
@@ -23525,7 +24585,7 @@ var ResearchCategory = z86.enum([
|
|
|
23525
24585
|
"garden",
|
|
23526
24586
|
"cleaning"
|
|
23527
24587
|
]);
|
|
23528
|
-
var HttpUrl3 =
|
|
24588
|
+
var HttpUrl3 = z90.string().url().max(2e3).refine((value) => {
|
|
23529
24589
|
try {
|
|
23530
24590
|
return [
|
|
23531
24591
|
"http:",
|
|
@@ -23535,45 +24595,45 @@ var HttpUrl3 = z86.string().url().max(2e3).refine((value) => {
|
|
|
23535
24595
|
return false;
|
|
23536
24596
|
}
|
|
23537
24597
|
}, "URL must use HTTP(S)");
|
|
23538
|
-
var ResearchCandidateOrigin =
|
|
23539
|
-
type:
|
|
24598
|
+
var ResearchCandidateOrigin = z90.object({
|
|
24599
|
+
type: z90.literal("research_candidate"),
|
|
23540
24600
|
runId: ProjectId,
|
|
23541
|
-
candidateSourceId:
|
|
24601
|
+
candidateSourceId: z90.string().min(1).max(160)
|
|
23542
24602
|
});
|
|
23543
|
-
var TaskOrigin =
|
|
23544
|
-
type:
|
|
24603
|
+
var TaskOrigin = z90.object({
|
|
24604
|
+
type: z90.literal("task"),
|
|
23545
24605
|
taskId: ProjectId
|
|
23546
24606
|
});
|
|
23547
|
-
var ManualOrigin =
|
|
23548
|
-
type:
|
|
24607
|
+
var ManualOrigin = z90.object({
|
|
24608
|
+
type: z90.literal("manual"),
|
|
23549
24609
|
clientRequestId: ProjectId,
|
|
23550
|
-
country:
|
|
24610
|
+
country: z90.string().min(2).max(3).optional()
|
|
23551
24611
|
});
|
|
23552
|
-
var StartProjectInput =
|
|
23553
|
-
origin:
|
|
24612
|
+
var StartProjectInput = z90.object({
|
|
24613
|
+
origin: z90.discriminatedUnion("type", [
|
|
23554
24614
|
ResearchCandidateOrigin,
|
|
23555
24615
|
TaskOrigin,
|
|
23556
24616
|
ManualOrigin
|
|
23557
24617
|
]),
|
|
23558
|
-
productName:
|
|
24618
|
+
productName: z90.string().trim().min(1).max(240).optional(),
|
|
23559
24619
|
productUrl: HttpUrl3.optional(),
|
|
23560
|
-
country:
|
|
23561
|
-
languageCode:
|
|
24620
|
+
country: z90.string().min(2).max(3).optional(),
|
|
24621
|
+
languageCode: z90.string().min(2).max(16).optional()
|
|
23562
24622
|
}).strict();
|
|
23563
|
-
var CostProjectInput =
|
|
24623
|
+
var CostProjectInput = z90.object({
|
|
23564
24624
|
projectId: ProjectId,
|
|
23565
|
-
version:
|
|
23566
|
-
country:
|
|
24625
|
+
version: z90.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
|
|
24626
|
+
country: z90.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
23567
24627
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
23568
24628
|
}).describe("C\xF3digo ISO-2 del pa\xEDs del proyecto. Usa el `targetCountry` que devuelve el proyecto."),
|
|
23569
|
-
supplierCost:
|
|
23570
|
-
shippingCost:
|
|
23571
|
-
returnRate:
|
|
23572
|
-
cancellationRate:
|
|
23573
|
-
adsManagerCpa:
|
|
23574
|
-
desiredMargin:
|
|
23575
|
-
});
|
|
23576
|
-
var GuideInput =
|
|
24629
|
+
supplierCost: z90.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."),
|
|
24630
|
+
shippingCost: z90.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
|
|
24631
|
+
returnRate: z90.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."),
|
|
24632
|
+
cancellationRate: z90.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."),
|
|
24633
|
+
adsManagerCpa: z90.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
|
|
24634
|
+
desiredMargin: z90.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
|
|
24635
|
+
});
|
|
24636
|
+
var GuideInput = z90.object({
|
|
23577
24637
|
projectId: ProjectId.optional()
|
|
23578
24638
|
}).strict();
|
|
23579
24639
|
var NEXT_ACTION_KINDS = [
|
|
@@ -23591,29 +24651,29 @@ var NEXT_ACTION_KINDS = [
|
|
|
23591
24651
|
"repair_meta_campaign",
|
|
23592
24652
|
"review_paused_campaign"
|
|
23593
24653
|
];
|
|
23594
|
-
var DropshippingProjectNextAction =
|
|
23595
|
-
kind:
|
|
23596
|
-
href:
|
|
24654
|
+
var DropshippingProjectNextAction = z90.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind) => z90.object({
|
|
24655
|
+
kind: z90.literal(kind),
|
|
24656
|
+
href: z90.string().min(1)
|
|
23597
24657
|
})));
|
|
23598
|
-
var CostingInputSchema =
|
|
23599
|
-
country:
|
|
23600
|
-
supplierCost:
|
|
23601
|
-
shippingCost:
|
|
23602
|
-
returnRate:
|
|
23603
|
-
cancellationRate:
|
|
23604
|
-
adsManagerCpa:
|
|
23605
|
-
desiredMargin:
|
|
24658
|
+
var CostingInputSchema = z90.object({
|
|
24659
|
+
country: z90.string().min(2).max(3),
|
|
24660
|
+
supplierCost: z90.number().finite().positive(),
|
|
24661
|
+
shippingCost: z90.number().finite().nonnegative(),
|
|
24662
|
+
returnRate: z90.number().finite().min(0).lt(1),
|
|
24663
|
+
cancellationRate: z90.number().finite().min(0).lt(1),
|
|
24664
|
+
adsManagerCpa: z90.number().finite().nonnegative(),
|
|
24665
|
+
desiredMargin: z90.number().finite().min(0).lt(1)
|
|
23606
24666
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
23607
24667
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
23608
|
-
code:
|
|
24668
|
+
code: z90.ZodIssueCode.custom,
|
|
23609
24669
|
path: [
|
|
23610
24670
|
"cancellationRate"
|
|
23611
24671
|
]
|
|
23612
24672
|
});
|
|
23613
24673
|
});
|
|
23614
|
-
var FiniteCostingNumber =
|
|
23615
|
-
var CostingResultSchema =
|
|
23616
|
-
country:
|
|
24674
|
+
var FiniteCostingNumber = z90.number().finite();
|
|
24675
|
+
var CostingResultSchema = z90.object({
|
|
24676
|
+
country: z90.string().min(2).max(3),
|
|
23617
24677
|
deliveryRate: FiniteCostingNumber,
|
|
23618
24678
|
adjustedShippingCost: FiniteCostingNumber,
|
|
23619
24679
|
adjustedCpa: FiniteCostingNumber,
|
|
@@ -23626,45 +24686,45 @@ var CostingResultSchema = z86.object({
|
|
|
23626
24686
|
estimatedProfit: FiniteCostingNumber,
|
|
23627
24687
|
actualMargin: FiniteCostingNumber,
|
|
23628
24688
|
breakEvenAdsManagerCpa: FiniteCostingNumber,
|
|
23629
|
-
wasPriceAutoAdjusted:
|
|
23630
|
-
profitabilityStatus:
|
|
23631
|
-
});
|
|
23632
|
-
var RewardProgressSchema =
|
|
23633
|
-
campaignKey:
|
|
23634
|
-
totalCredits:
|
|
23635
|
-
earnedCredits:
|
|
23636
|
-
milestones:
|
|
24689
|
+
wasPriceAutoAdjusted: z90.boolean(),
|
|
24690
|
+
profitabilityStatus: z90.literal("healthy")
|
|
24691
|
+
});
|
|
24692
|
+
var RewardProgressSchema = z90.object({
|
|
24693
|
+
campaignKey: z90.literal("dropshipping-launch-v1"),
|
|
24694
|
+
totalCredits: z90.number().int().positive(),
|
|
24695
|
+
earnedCredits: z90.number().int().nonnegative(),
|
|
24696
|
+
milestones: z90.array(z90.object({
|
|
23637
24697
|
milestone: DropshippingRewardMilestone,
|
|
23638
|
-
credits:
|
|
23639
|
-
available:
|
|
23640
|
-
earned:
|
|
24698
|
+
credits: z90.number().int().positive(),
|
|
24699
|
+
available: z90.boolean().optional(),
|
|
24700
|
+
earned: z90.boolean(),
|
|
23641
24701
|
grantId: ProjectId.nullable(),
|
|
23642
|
-
grantedAt:
|
|
24702
|
+
grantedAt: z90.string().datetime().nullable()
|
|
23643
24703
|
})).length(DropshippingRewardMilestone.options.length)
|
|
23644
24704
|
});
|
|
23645
|
-
var ProjectResponseSchema =
|
|
24705
|
+
var ProjectResponseSchema = z90.object({
|
|
23646
24706
|
id: ProjectId,
|
|
23647
24707
|
taskId: ProjectId.nullable(),
|
|
23648
24708
|
status: DropshippingProjectStatus,
|
|
23649
|
-
originKey:
|
|
24709
|
+
originKey: z90.string().min(1),
|
|
23650
24710
|
researchRunId: ProjectId.nullable(),
|
|
23651
|
-
candidateSourceId:
|
|
23652
|
-
productName:
|
|
24711
|
+
candidateSourceId: z90.string().nullable(),
|
|
24712
|
+
productName: z90.string().nullable(),
|
|
23653
24713
|
productUrl: HttpUrl3.nullable(),
|
|
23654
24714
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
23655
|
-
targetCountry:
|
|
23656
|
-
languageCode:
|
|
23657
|
-
hasReferenceImage:
|
|
23658
|
-
hasWinnerReferenceImage:
|
|
24715
|
+
targetCountry: z90.string().nullable(),
|
|
24716
|
+
languageCode: z90.string().nullable(),
|
|
24717
|
+
hasReferenceImage: z90.boolean(),
|
|
24718
|
+
hasWinnerReferenceImage: z90.boolean(),
|
|
23659
24719
|
costingInput: CostingInputSchema.nullable(),
|
|
23660
24720
|
costingResult: CostingResultSchema.nullable(),
|
|
23661
|
-
costingVersion:
|
|
23662
|
-
version:
|
|
24721
|
+
costingVersion: z90.number().int().positive().nullable(),
|
|
24722
|
+
version: z90.number().int().positive(),
|
|
23663
24723
|
phase: DropshippingProjectPhase,
|
|
23664
24724
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
23665
24725
|
rewards: RewardProgressSchema,
|
|
23666
|
-
createdAt:
|
|
23667
|
-
updatedAt:
|
|
24726
|
+
createdAt: z90.string().datetime(),
|
|
24727
|
+
updatedAt: z90.string().datetime()
|
|
23668
24728
|
}).passthrough();
|
|
23669
24729
|
var NEXT_ACTION_GUIDANCE = {
|
|
23670
24730
|
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.",
|
|
@@ -23782,58 +24842,58 @@ function registerDropshippingProjectTools(server, api) {
|
|
|
23782
24842
|
__name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
|
|
23783
24843
|
|
|
23784
24844
|
// src/tools/daily-control.ts
|
|
23785
|
-
import { z as
|
|
24845
|
+
import { z as z91 } from "zod";
|
|
23786
24846
|
var getDailyControlGuidance = getProductMakerCapabilityModelGuidance("get_daily_control");
|
|
23787
24847
|
var linkCampaignToProjectGuidance = getProductMakerCapabilityModelGuidance("link_campaign_to_project");
|
|
23788
24848
|
var reportDailyReturnsGuidance = getProductMakerCapabilityModelGuidance("report_daily_returns");
|
|
23789
|
-
var ProviderCampaignIdentifier =
|
|
23790
|
-
var DateOnly =
|
|
23791
|
-
var DailyControlInput =
|
|
24849
|
+
var ProviderCampaignIdentifier = z91.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
|
|
24850
|
+
var DateOnly = z91.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
24851
|
+
var DailyControlInput = z91.object({
|
|
23792
24852
|
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."),
|
|
23793
|
-
days:
|
|
23794
|
-
projectId:
|
|
24853
|
+
days: z91.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
|
|
24854
|
+
projectId: z91.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
|
|
23795
24855
|
}).strict();
|
|
23796
|
-
var LinkCampaignInput =
|
|
23797
|
-
projectId:
|
|
23798
|
-
provider:
|
|
24856
|
+
var LinkCampaignInput = z91.object({
|
|
24857
|
+
projectId: z91.string().uuid(),
|
|
24858
|
+
provider: z91.enum([
|
|
23799
24859
|
"meta",
|
|
23800
24860
|
"tiktok"
|
|
23801
24861
|
]),
|
|
23802
24862
|
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."),
|
|
23803
24863
|
campaignId: ProviderCampaignIdentifier
|
|
23804
24864
|
}).strict();
|
|
23805
|
-
var ReportReturnsInput =
|
|
23806
|
-
projectId:
|
|
24865
|
+
var ReportReturnsInput = z91.object({
|
|
24866
|
+
projectId: z91.string().uuid(),
|
|
23807
24867
|
date: DateOnly,
|
|
23808
|
-
returnedOrders:
|
|
23809
|
-
note:
|
|
24868
|
+
returnedOrders: z91.number().int().min(0).max(1e5),
|
|
24869
|
+
note: z91.string().trim().max(280).optional()
|
|
23810
24870
|
}).strict();
|
|
23811
|
-
var DailyControlStatusMirror = DailyControlStatusEnum.or(
|
|
23812
|
-
var DailyControlLightMirror = DailyControlLightEnum.or(
|
|
23813
|
-
var DailyControlSourceMirror = DailyControlSourceEnum.or(
|
|
23814
|
-
var DailyControlVerdictMirror = DailyControlVerdictEnum.or(
|
|
23815
|
-
var DailyControlDayMirror =
|
|
23816
|
-
date:
|
|
23817
|
-
returnedOrders:
|
|
24871
|
+
var DailyControlStatusMirror = DailyControlStatusEnum.or(z91.string());
|
|
24872
|
+
var DailyControlLightMirror = DailyControlLightEnum.or(z91.string());
|
|
24873
|
+
var DailyControlSourceMirror = DailyControlSourceEnum.or(z91.string());
|
|
24874
|
+
var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z91.string());
|
|
24875
|
+
var DailyControlDayMirror = z91.object({
|
|
24876
|
+
date: z91.string(),
|
|
24877
|
+
returnedOrders: z91.number(),
|
|
23818
24878
|
returnedSource: DailyControlSourceMirror,
|
|
23819
24879
|
cancelledSource: DailyControlSourceMirror,
|
|
23820
|
-
hasEstimates:
|
|
24880
|
+
hasEstimates: z91.boolean(),
|
|
23821
24881
|
light: DailyControlLightMirror
|
|
23822
24882
|
}).passthrough();
|
|
23823
|
-
var DailyControlProductMirror =
|
|
23824
|
-
projectId:
|
|
23825
|
-
productName:
|
|
24883
|
+
var DailyControlProductMirror = z91.object({
|
|
24884
|
+
projectId: z91.string().uuid(),
|
|
24885
|
+
productName: z91.string().nullable(),
|
|
23826
24886
|
status: DailyControlStatusMirror,
|
|
23827
|
-
statusMessage:
|
|
23828
|
-
days:
|
|
24887
|
+
statusMessage: z91.string().nullable(),
|
|
24888
|
+
days: z91.array(DailyControlDayMirror),
|
|
23829
24889
|
verdict: DailyControlVerdictMirror.nullable(),
|
|
23830
|
-
rationale:
|
|
24890
|
+
rationale: z91.string().nullable()
|
|
23831
24891
|
}).passthrough();
|
|
23832
|
-
var DailyControlResponseMirror =
|
|
23833
|
-
since:
|
|
23834
|
-
until:
|
|
23835
|
-
products:
|
|
23836
|
-
archivedProjectSpend:
|
|
24892
|
+
var DailyControlResponseMirror = z91.object({
|
|
24893
|
+
since: z91.string(),
|
|
24894
|
+
until: z91.string(),
|
|
24895
|
+
products: z91.array(DailyControlProductMirror),
|
|
24896
|
+
archivedProjectSpend: z91.array(z91.unknown())
|
|
23837
24897
|
}).passthrough();
|
|
23838
24898
|
var MCP_WARNINGS_KEY = "_mcpDailyControlWarnings";
|
|
23839
24899
|
function withWarnings(response) {
|
|
@@ -23917,42 +24977,42 @@ function registerDailyControlTools(server, api) {
|
|
|
23917
24977
|
__name(registerDailyControlTools, "registerDailyControlTools");
|
|
23918
24978
|
|
|
23919
24979
|
// src/tools/product-control.ts
|
|
23920
|
-
import { z as
|
|
24980
|
+
import { z as z92 } from "zod";
|
|
23921
24981
|
var getProductControlGuidance = getProductMakerCapabilityModelGuidance("get_product_control");
|
|
23922
24982
|
var listImportableProductsGuidance = getProductMakerCapabilityModelGuidance("list_importable_products");
|
|
23923
24983
|
var importProductControlsGuidance = getProductMakerCapabilityModelGuidance("import_product_controls");
|
|
23924
24984
|
var setProductCostingGuidance = getProductMakerCapabilityModelGuidance("set_product_costing");
|
|
23925
24985
|
var reportProductCountsGuidance = getProductMakerCapabilityModelGuidance("report_product_counts");
|
|
23926
|
-
var DateOnly2 =
|
|
24986
|
+
var DateOnly2 = z92.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
23927
24987
|
var IMPORT_BATCH_MAX2 = 50;
|
|
23928
|
-
var ProductControlListInput =
|
|
23929
|
-
days:
|
|
24988
|
+
var ProductControlListInput = z92.object({
|
|
24989
|
+
days: z92.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
|
|
23930
24990
|
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."),
|
|
23931
|
-
shopDomain:
|
|
24991
|
+
shopDomain: z92.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
|
|
23932
24992
|
}).strict();
|
|
23933
|
-
var ImportableProductsInput =
|
|
23934
|
-
q:
|
|
23935
|
-
after:
|
|
23936
|
-
shopDomain:
|
|
24993
|
+
var ImportableProductsInput = z92.object({
|
|
24994
|
+
q: z92.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
|
|
24995
|
+
after: z92.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
|
|
24996
|
+
shopDomain: z92.string().min(1).optional()
|
|
23937
24997
|
}).strict();
|
|
23938
|
-
var ImportProductControlsInput =
|
|
23939
|
-
shopifyProductIds:
|
|
24998
|
+
var ImportProductControlsInput = z92.object({
|
|
24999
|
+
shopifyProductIds: z92.array(z92.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."),
|
|
23940
25000
|
// Obligatorio, como en el endpoint: con dos tiendas conectadas, importar
|
|
23941
25001
|
// sin decir a cuál archivaría el producto bajo la tienda equivocada.
|
|
23942
|
-
shopDomain:
|
|
25002
|
+
shopDomain: z92.string().min(1)
|
|
23943
25003
|
}).strict();
|
|
23944
|
-
var SetProductCostingInput =
|
|
23945
|
-
shopDomain:
|
|
23946
|
-
shopifyProductId:
|
|
23947
|
-
targetCountry:
|
|
23948
|
-
supplierCost:
|
|
23949
|
-
shippingCost:
|
|
23950
|
-
returnRate:
|
|
23951
|
-
cancellationRate:
|
|
25004
|
+
var SetProductCostingInput = z92.object({
|
|
25005
|
+
shopDomain: z92.string().min(1),
|
|
25006
|
+
shopifyProductId: z92.string().min(1).max(64),
|
|
25007
|
+
targetCountry: z92.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
|
|
25008
|
+
supplierCost: z92.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
|
|
25009
|
+
shippingCost: z92.number().finite().nonnegative(),
|
|
25010
|
+
returnRate: z92.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
|
|
25011
|
+
cancellationRate: z92.number().finite().min(0).lt(1)
|
|
23952
25012
|
}).strict();
|
|
23953
|
-
var Count =
|
|
23954
|
-
var ReportProductCountsInput =
|
|
23955
|
-
productControlId:
|
|
25013
|
+
var Count = z92.number().int().min(0).max(1e5).nullable().optional();
|
|
25014
|
+
var ReportProductCountsInput = z92.object({
|
|
25015
|
+
productControlId: z92.string().uuid().describe("El identificador que devuelve get_product_control."),
|
|
23956
25016
|
date: DateOnly2,
|
|
23957
25017
|
orders: Count.describe("Pedidos que entraron ese d\xEDa."),
|
|
23958
25018
|
cancelledOrders: Count,
|
|
@@ -24112,6 +25172,16 @@ function firstHeader(value) {
|
|
|
24112
25172
|
}
|
|
24113
25173
|
__name(firstHeader, "firstHeader");
|
|
24114
25174
|
var PROXY_FETCH_TIMEOUT_MS = 3e4;
|
|
25175
|
+
var DEFAULT_SUBJECT_TOKEN_REAUTH_WINDOW_MS = 18e4;
|
|
25176
|
+
function subjectTokenReauthWindowMs(env) {
|
|
25177
|
+
const raw = env.MCP_SUBJECT_TOKEN_REAUTH_WINDOW_MS;
|
|
25178
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
25179
|
+
return DEFAULT_SUBJECT_TOKEN_REAUTH_WINDOW_MS;
|
|
25180
|
+
}
|
|
25181
|
+
const parsed = Number.parseInt(raw, 10);
|
|
25182
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_SUBJECT_TOKEN_REAUTH_WINDOW_MS;
|
|
25183
|
+
}
|
|
25184
|
+
__name(subjectTokenReauthWindowMs, "subjectTokenReauthWindowMs");
|
|
24115
25185
|
var INLINE_RENDERABLE_MIMES = /* @__PURE__ */ new Set([
|
|
24116
25186
|
"image/jpeg",
|
|
24117
25187
|
"image/png",
|
|
@@ -24242,7 +25312,8 @@ function resolveDownstreamAuth(req, opts, oauthAnalytics) {
|
|
|
24242
25312
|
credential,
|
|
24243
25313
|
subject,
|
|
24244
25314
|
clientId,
|
|
24245
|
-
isLegacy: false
|
|
25315
|
+
isLegacy: false,
|
|
25316
|
+
subjectExpiresAt: expiresAt
|
|
24246
25317
|
};
|
|
24247
25318
|
}
|
|
24248
25319
|
__name(resolveDownstreamAuth, "resolveDownstreamAuth");
|
|
@@ -24266,6 +25337,14 @@ function createAuthenticatedMcpHandler(opts, oauthAnalytics) {
|
|
|
24266
25337
|
return;
|
|
24267
25338
|
}
|
|
24268
25339
|
const { credential, subject, clientId, isLegacy } = authContext;
|
|
25340
|
+
const reauthWindowMs = subjectTokenReauthWindowMs(process.env);
|
|
25341
|
+
if (reauthWindowMs > 0 && !isLegacy && typeof authContext.subjectExpiresAt === "number" && authContext.subjectExpiresAt * 1e3 <= Date.now() + reauthWindowMs) {
|
|
25342
|
+
res.status(401).set("WWW-Authenticate", `Bearer error="invalid_token", error_description="Access token expired or expiring soon", scope="${MCP_SCOPE}", resource_metadata="${getOAuthProtectedResourceMetadataUrl2(MCP_RESOURCE)}"`).json({
|
|
25343
|
+
error: "invalid_token",
|
|
25344
|
+
error_description: "Access token expired or expiring soon; refresh or re-authenticate."
|
|
25345
|
+
});
|
|
25346
|
+
return;
|
|
25347
|
+
}
|
|
24269
25348
|
const abortController = new AbortController();
|
|
24270
25349
|
const abortRequest = /* @__PURE__ */ __name(() => abortController.abort(), "abortRequest");
|
|
24271
25350
|
req.once("aborted", abortRequest);
|