@clipwright/core 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/billing-terms.d.ts +19 -0
  3. package/dist/billing-terms.js +8 -0
  4. package/dist/billing-terms.js.map +1 -0
  5. package/dist/broll-policy.d.ts +31 -0
  6. package/dist/broll-policy.js +24 -0
  7. package/dist/broll-policy.js.map +1 -0
  8. package/dist/contract-dispositions.d.ts +65 -3
  9. package/dist/contract-dispositions.js +25 -21
  10. package/dist/contract-dispositions.js.map +1 -1
  11. package/dist/endpoints.d.ts +0 -2
  12. package/dist/endpoints.js +5 -0
  13. package/dist/endpoints.js.map +1 -1
  14. package/dist/face-billing.d.ts +116 -0
  15. package/dist/face-billing.js +83 -0
  16. package/dist/face-billing.js.map +1 -0
  17. package/dist/face-delivery.d.ts +17 -0
  18. package/dist/face-delivery.js +12 -0
  19. package/dist/face-delivery.js.map +1 -0
  20. package/dist/index.d.ts +9 -0
  21. package/dist/index.js +9 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/long-form-pricing.d.ts +48 -0
  24. package/dist/long-form-pricing.js +73 -0
  25. package/dist/long-form-pricing.js.map +1 -0
  26. package/dist/long-form-profile.d.ts +5 -0
  27. package/dist/long-form-profile.js +6 -0
  28. package/dist/long-form-profile.js.map +1 -0
  29. package/dist/media-timebase.d.ts +2 -0
  30. package/dist/media-timebase.js +3 -0
  31. package/dist/media-timebase.js.map +1 -0
  32. package/dist/public-endpoints.d.ts +2 -0
  33. package/dist/public-endpoints.js +2 -0
  34. package/dist/public-endpoints.js.map +1 -1
  35. package/dist/render-backend.d.ts +3 -1
  36. package/dist/runs.d.ts +106 -0
  37. package/dist/runs.js +12 -0
  38. package/dist/runs.js.map +1 -1
  39. package/dist/semantic-inserts.d.ts +63 -0
  40. package/dist/semantic-inserts.js +160 -0
  41. package/dist/semantic-inserts.js.map +1 -0
  42. package/dist/skills.d.ts +210 -0
  43. package/dist/skills.js +69 -12
  44. package/dist/skills.js.map +1 -1
  45. package/dist/speech-rhythm-contract.d.ts +20 -0
  46. package/dist/speech-rhythm-contract.js +22 -0
  47. package/dist/speech-rhythm-contract.js.map +1 -0
  48. package/dist/speech-rhythm.d.ts +21 -0
  49. package/dist/speech-rhythm.js +47 -0
  50. package/dist/speech-rhythm.js.map +1 -0
  51. package/dist/tts-backend.d.ts +1 -0
  52. package/dist/tts-backend.js.map +1 -1
  53. package/dist/voice-admission.d.ts +40 -0
  54. package/dist/voice-preview-approval.d.ts +27 -0
  55. package/dist/voice-preview-approval.js +29 -0
  56. package/dist/voice-preview-approval.js.map +1 -0
  57. package/package.json +13 -1
@@ -0,0 +1,160 @@
1
+ /** Первое НЕЗАКРЫТОЕ вставкой слово. Умолчание читается здесь и только здесь, */
2
+ /** иначе «покрытие равно якорю» разъедется между читателями (#265). */
3
+ export function coverWordOf(anchor) {
4
+ return anchor.coverWord ?? anchor.endWord;
5
+ }
6
+ function tokenize(text) {
7
+ const normalized = text.normalize("NFD").toLowerCase()
8
+ .replace(/([аеиоуыэюя]\u0308?)\u0301/gu, "$1").normalize("NFC")
9
+ .replace(/[’ʼ]/gu, "'");
10
+ return normalized.match(/[\p{L}\p{N}][\p{L}\p{N}\p{M}]*(?:['-][\p{L}\p{N}][\p{L}\p{N}\p{M}]*|[.,](?<=\p{N}[.,])\p{N}+)*/gu) ?? [];
11
+ }
12
+ function orderedAnchors(plan) {
13
+ if (plan.sourceTokens.length === 0 || plan.sourceTokens.some(token => typeof token !== "string" || tokenize(token).length !== 1 || tokenize(token)[0] !== token)) {
14
+ throw new Error("Semantic plan requires normalized source tokens");
15
+ }
16
+ const ids = new Set();
17
+ for (const anchor of plan.inserts) {
18
+ if (typeof anchor.insertId !== "string" || !anchor.insertId.trim() ||
19
+ typeof anchor.mediaId !== "string" || !anchor.mediaId.trim() || ids.has(anchor.insertId)) {
20
+ throw new Error("Semantic inserts require unique nonempty insert IDs and media IDs");
21
+ }
22
+ ids.add(anchor.insertId);
23
+ if (!Number.isSafeInteger(anchor.startWord) || !Number.isSafeInteger(anchor.endWord) ||
24
+ anchor.startWord < 0 || anchor.endWord <= anchor.startWord || anchor.endWord > plan.sourceTokens.length) {
25
+ throw new Error("Semantic insert word range is invalid");
26
+ }
27
+ if (anchor.coverWord !== undefined && (!Number.isSafeInteger(anchor.coverWord) ||
28
+ anchor.coverWord < anchor.endWord || anchor.coverWord > plan.sourceTokens.length)) {
29
+ throw new Error("Semantic insert coverage word is invalid");
30
+ }
31
+ }
32
+ const ordered = plan.inserts.map(anchor => ({ ...anchor })).sort((a, b) => a.startWord - b.startWord);
33
+ for (let i = 1; i < ordered.length; i++) {
34
+ if (ordered[i].startWord < coverWordOf(ordered[i - 1]))
35
+ throw new Error("Semantic insert word ranges overlap");
36
+ }
37
+ return ordered;
38
+ }
39
+ export function planSemanticInserts(script, instructions) {
40
+ for (const chunk of script.match(/\S+/gu) ?? []) {
41
+ const tokens = tokenize(chunk);
42
+ if (tokens.length > 1 || (tokens.length === 0 && !/^\p{P}+$/u.test(chunk))) {
43
+ throw new Error("Unsupported script tokenization: each whitespace chunk must contain at most one lexical word");
44
+ }
45
+ }
46
+ const sourceTokens = tokenize(script);
47
+ function quoteRange(anchor) {
48
+ const quoteTokens = tokenize(anchor.quote);
49
+ if (quoteTokens.length === 0)
50
+ throw new Error("Semantic insert quote is empty");
51
+ const matches = [];
52
+ for (let i = 0; i <= sourceTokens.length - quoteTokens.length; i++) {
53
+ if (quoteTokens.every((token, offset) => token === sourceTokens[i + offset]))
54
+ matches.push(i);
55
+ }
56
+ if (anchor.occurrence === undefined && matches.length > 1)
57
+ throw new Error("Semantic insert quote is ambiguous");
58
+ const occurrence = anchor.occurrence ?? 0;
59
+ if (!Number.isSafeInteger(occurrence) || occurrence < 0 || occurrence >= matches.length) {
60
+ throw new Error("Semantic insert quote occurrence does not match source text");
61
+ }
62
+ const startWord = matches[occurrence];
63
+ return { startWord, endWord: startWord + quoteTokens.length };
64
+ }
65
+ const inserts = instructions.map(({ insertId, mediaId, anchor, coverWords }) => {
66
+ const range = "startWord" in anchor
67
+ ? { startWord: anchor.startWord, endWord: anchor.endWord }
68
+ : quoteRange(anchor);
69
+ if (coverWords === undefined)
70
+ return { insertId, mediaId, ...range };
71
+ const anchored = range.endWord - range.startWord;
72
+ if (!Number.isSafeInteger(coverWords) || coverWords < anchored) {
73
+ throw new Error(`Semantic insert covers ${coverWords} words, fewer than the ${anchored} words it anchors`);
74
+ }
75
+ const coverWord = range.startWord + coverWords;
76
+ return { insertId, mediaId, ...range, ...(coverWord === range.endWord ? {} : { coverWord }) };
77
+ });
78
+ const plan = { sourceTokens, inserts };
79
+ return { sourceTokens, inserts: orderedAnchors(plan) };
80
+ }
81
+ /** Число ПЛАТНЫХ актёрских шотов — чистая функция индексов слов, поэтому вход */
82
+ /** отвергается до синтеза речи, а не в воркере после оплаты (#265). */
83
+ export function countActorShots(plan) {
84
+ const anchors = orderedAnchors(plan);
85
+ if (anchors.length === 0)
86
+ return 1;
87
+ let shots = anchors[0].startWord > 0 ? 1 : 0;
88
+ for (let i = 1; i < anchors.length; i++) {
89
+ if (coverWordOf(anchors[i - 1]) < anchors[i].startWord)
90
+ shots++;
91
+ }
92
+ return shots + (coverWordOf(anchors[anchors.length - 1]) < plan.sourceTokens.length ? 1 : 0);
93
+ }
94
+ export function resolveSemanticInserts(input) {
95
+ const { plan, words, durationSec, fps } = input;
96
+ const anchors = orderedAnchors(plan);
97
+ if (!Number.isFinite(durationSec) || durationSec <= 0 || !Number.isSafeInteger(fps) || fps <= 0) {
98
+ throw new Error("Semantic resolution requires a measured duration and positive integer fps");
99
+ }
100
+ const totalFrames = Math.round(durationSec * fps);
101
+ if (!Number.isSafeInteger(totalFrames) || totalFrames <= 0)
102
+ throw new Error("Measured duration has no representable frames");
103
+ const lexicalWords = [];
104
+ let previousEnd = 0;
105
+ for (let i = 0; i < words.length; i++) {
106
+ const word = words[i];
107
+ const tokens = tokenize(word.word);
108
+ const punctuationOnly = /^\p{P}+$/u.test(word.word);
109
+ if (!Number.isFinite(word.startSec) || !Number.isFinite(word.endSec) || word.startSec < previousEnd ||
110
+ word.endSec < word.startSec || (!punctuationOnly && word.endSec === word.startSec) || word.endSec > durationSec) {
111
+ throw new Error("Alignment timings are invalid, overlap, or exceed measured duration");
112
+ }
113
+ previousEnd = word.endSec;
114
+ if (punctuationOnly)
115
+ continue;
116
+ if (tokens.length !== 1 || tokens[0] !== plan.sourceTokens[lexicalWords.length]) {
117
+ throw new Error("Alignment text does not match source tokens");
118
+ }
119
+ lexicalWords.push(word);
120
+ }
121
+ if (lexicalWords.length !== plan.sourceTokens.length)
122
+ throw new Error("Alignment does not cover the source tokens exactly");
123
+ const shots = [];
124
+ let cursor = 0;
125
+ let rounded = Math.abs(totalFrames - durationSec * fps) > 1e-8;
126
+ for (const anchor of anchors) {
127
+ // Пауза принадлежит владельцу предыдущего слова: окно кончается на НАЧАЛЕ
128
+ // первого незакрытого слова, иначе щель станет платным огрызком (#265).
129
+ const cover = coverWordOf(anchor);
130
+ const head = anchor.startWord === 0;
131
+ const tail = cover >= lexicalWords.length;
132
+ const start = lexicalWords[anchor.startWord].startSec * fps;
133
+ const end = tail ? totalFrames : lexicalWords[cover].startSec * fps;
134
+ const fromFrame = head ? 0 : Math.round(start);
135
+ const endFrame = tail ? totalFrames : Math.round(end);
136
+ if (fromFrame < cursor || endFrame <= fromFrame || endFrame > totalFrames) {
137
+ throw new Error("Semantic insert frame range collides or has no positive duration");
138
+ }
139
+ if (!head)
140
+ rounded ||= Math.abs(fromFrame - start) > 1e-8;
141
+ if (!tail)
142
+ rounded ||= Math.abs(endFrame - end) > 1e-8;
143
+ if (fromFrame > cursor)
144
+ shots.push({ kind: "actor", fromFrame: cursor, durationInFrames: fromFrame - cursor });
145
+ shots.push({ kind: "media", fromFrame, durationInFrames: endFrame - fromFrame, insertId: anchor.insertId, mediaId: anchor.mediaId });
146
+ cursor = endFrame;
147
+ }
148
+ if (cursor < totalFrames)
149
+ shots.push({ kind: "actor", fromFrame: cursor, durationInFrames: totalFrames - cursor });
150
+ const warnings = [];
151
+ if (rounded)
152
+ warnings.push("Semantic insert boundaries or measured duration were rounded to the nearest frame.");
153
+ if (lexicalWords.length !== words.length)
154
+ warnings.push("Punctuation-only alignment entries were excluded from semantic word anchors.");
155
+ return { totalFrames, shots, warnings };
156
+ }
157
+ export function selectTimelineInput(segments, planner) {
158
+ return segments !== undefined ? { kind: "explicit", segments } : { kind: "semantic", plan: planner() };
159
+ }
160
+ //# sourceMappingURL=semantic-inserts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-inserts.js","sourceRoot":"","sources":["../src/semantic-inserts.ts"],"names":[],"mappings":"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"}
package/dist/skills.d.ts CHANGED
@@ -82,11 +82,51 @@ export declare const ugcSegment: z.ZodObject<{
82
82
  }>;
83
83
  script: z.ZodOptional<z.ZodString>;
84
84
  media_url: z.ZodOptional<z.ZodString>;
85
+ media_type: z.ZodOptional<z.ZodEnum<{
86
+ image: "image";
87
+ video: "video";
88
+ }>>;
89
+ duration_seconds: z.ZodOptional<z.ZodNumber>;
90
+ image_motion: z.ZodOptional<z.ZodObject<{
91
+ direction: z.ZodEnum<{
92
+ in: "in";
93
+ out: "out";
94
+ }>;
95
+ amount: z.ZodNumber;
96
+ }, z.core.$strip>>;
85
97
  }, z.core.$strip>;
86
98
  export type UgcSegment = z.infer<typeof ugcSegment>;
99
+ export declare const ugcInsert: z.ZodObject<{
100
+ id: z.ZodString;
101
+ media_url: z.ZodString;
102
+ media_type: z.ZodEnum<{
103
+ image: "image";
104
+ video: "video";
105
+ }>;
106
+ anchor: z.ZodUnion<readonly [z.ZodObject<{
107
+ startWord: z.ZodNumber;
108
+ endWord: z.ZodNumber;
109
+ }, z.core.$strict>, z.ZodObject<{
110
+ quote: z.ZodString;
111
+ occurrence: z.ZodOptional<z.ZodNumber>;
112
+ }, z.core.$strict>]>;
113
+ cover_words: z.ZodOptional<z.ZodNumber>;
114
+ image_motion: z.ZodOptional<z.ZodObject<{
115
+ direction: z.ZodEnum<{
116
+ in: "in";
117
+ out: "out";
118
+ }>;
119
+ amount: z.ZodNumber;
120
+ }, z.core.$strict>>;
121
+ }, z.core.$strict>;
122
+ export type UgcInsert = z.infer<typeof ugcInsert>;
87
123
  /** Потолок актёрских сегментов — ограничение ОДНОГО рана, не суточных трат. */
88
124
  export declare const MAX_ACTOR_SEGMENTS = 3;
89
125
  export declare const MAX_SEGMENTS = 5;
126
+ /** Потолки НАРРАТИВНОГО пути — другие, потому что медиа-шот денег не стоит, */
127
+ /** а актёрский стоит: вставок столько, сколько вмещает таймлайн (ADR #265). */
128
+ export declare const MAX_INSERTS: number;
129
+ export declare const MAX_NARRATED_ACTOR_SHOTS = 6;
90
130
  export declare const actorId: z.ZodString;
91
131
  export declare const makeUgcInputShape: {
92
132
  readonly script: z.ZodOptional<z.ZodString>;
@@ -97,7 +137,42 @@ export declare const makeUgcInputShape: {
97
137
  }>;
98
138
  script: z.ZodOptional<z.ZodString>;
99
139
  media_url: z.ZodOptional<z.ZodString>;
140
+ media_type: z.ZodOptional<z.ZodEnum<{
141
+ image: "image";
142
+ video: "video";
143
+ }>>;
144
+ duration_seconds: z.ZodOptional<z.ZodNumber>;
145
+ image_motion: z.ZodOptional<z.ZodObject<{
146
+ direction: z.ZodEnum<{
147
+ in: "in";
148
+ out: "out";
149
+ }>;
150
+ amount: z.ZodNumber;
151
+ }, z.core.$strip>>;
100
152
  }, z.core.$strip>>>;
153
+ readonly inserts: z.ZodOptional<z.ZodArray<z.ZodObject<{
154
+ id: z.ZodString;
155
+ media_url: z.ZodString;
156
+ media_type: z.ZodEnum<{
157
+ image: "image";
158
+ video: "video";
159
+ }>;
160
+ anchor: z.ZodUnion<readonly [z.ZodObject<{
161
+ startWord: z.ZodNumber;
162
+ endWord: z.ZodNumber;
163
+ }, z.core.$strict>, z.ZodObject<{
164
+ quote: z.ZodString;
165
+ occurrence: z.ZodOptional<z.ZodNumber>;
166
+ }, z.core.$strict>]>;
167
+ cover_words: z.ZodOptional<z.ZodNumber>;
168
+ image_motion: z.ZodOptional<z.ZodObject<{
169
+ direction: z.ZodEnum<{
170
+ in: "in";
171
+ out: "out";
172
+ }>;
173
+ amount: z.ZodNumber;
174
+ }, z.core.$strict>>;
175
+ }, z.core.$strict>>>;
101
176
  readonly person: z.ZodOptional<z.ZodString>;
102
177
  readonly actor_id: z.ZodOptional<z.ZodString>;
103
178
  readonly image: z.ZodOptional<z.ZodString>;
@@ -108,6 +183,11 @@ export declare const makeUgcInputShape: {
108
183
  readonly character: z.ZodOptional<z.ZodString>;
109
184
  readonly name: z.ZodOptional<z.ZodString>;
110
185
  readonly broll_url: z.ZodOptional<z.ZodString>;
186
+ readonly broll_policy: z.ZodOptional<z.ZodEnum<{
187
+ anyone: "anyone";
188
+ no_actor: "no_actor";
189
+ no_people: "no_people";
190
+ }>>;
111
191
  readonly captions: z.ZodDefault<z.ZodBoolean>;
112
192
  readonly caption_style: z.ZodDefault<z.ZodEnum<{
113
193
  hormozi: "hormozi";
@@ -182,7 +262,42 @@ export declare const makeUgcInput: z.ZodObject<{
182
262
  }>;
183
263
  script: z.ZodOptional<z.ZodString>;
184
264
  media_url: z.ZodOptional<z.ZodString>;
265
+ media_type: z.ZodOptional<z.ZodEnum<{
266
+ image: "image";
267
+ video: "video";
268
+ }>>;
269
+ duration_seconds: z.ZodOptional<z.ZodNumber>;
270
+ image_motion: z.ZodOptional<z.ZodObject<{
271
+ direction: z.ZodEnum<{
272
+ in: "in";
273
+ out: "out";
274
+ }>;
275
+ amount: z.ZodNumber;
276
+ }, z.core.$strip>>;
185
277
  }, z.core.$strip>>>;
278
+ inserts: z.ZodOptional<z.ZodArray<z.ZodObject<{
279
+ id: z.ZodString;
280
+ media_url: z.ZodString;
281
+ media_type: z.ZodEnum<{
282
+ image: "image";
283
+ video: "video";
284
+ }>;
285
+ anchor: z.ZodUnion<readonly [z.ZodObject<{
286
+ startWord: z.ZodNumber;
287
+ endWord: z.ZodNumber;
288
+ }, z.core.$strict>, z.ZodObject<{
289
+ quote: z.ZodString;
290
+ occurrence: z.ZodOptional<z.ZodNumber>;
291
+ }, z.core.$strict>]>;
292
+ cover_words: z.ZodOptional<z.ZodNumber>;
293
+ image_motion: z.ZodOptional<z.ZodObject<{
294
+ direction: z.ZodEnum<{
295
+ in: "in";
296
+ out: "out";
297
+ }>;
298
+ amount: z.ZodNumber;
299
+ }, z.core.$strict>>;
300
+ }, z.core.$strict>>>;
186
301
  person: z.ZodOptional<z.ZodString>;
187
302
  actor_id: z.ZodOptional<z.ZodString>;
188
303
  image: z.ZodOptional<z.ZodString>;
@@ -193,6 +308,11 @@ export declare const makeUgcInput: z.ZodObject<{
193
308
  character: z.ZodOptional<z.ZodString>;
194
309
  name: z.ZodOptional<z.ZodString>;
195
310
  broll_url: z.ZodOptional<z.ZodString>;
311
+ broll_policy: z.ZodOptional<z.ZodEnum<{
312
+ anyone: "anyone";
313
+ no_actor: "no_actor";
314
+ no_people: "no_people";
315
+ }>>;
196
316
  captions: z.ZodDefault<z.ZodBoolean>;
197
317
  caption_style: z.ZodDefault<z.ZodEnum<{
198
318
  hormozi: "hormozi";
@@ -240,7 +360,42 @@ export declare function serverUgcInputWith(voiceRefusal: (voice: string) => stri
240
360
  }>;
241
361
  script: z.ZodOptional<z.ZodString>;
242
362
  media_url: z.ZodOptional<z.ZodString>;
363
+ media_type: z.ZodOptional<z.ZodEnum<{
364
+ image: "image";
365
+ video: "video";
366
+ }>>;
367
+ duration_seconds: z.ZodOptional<z.ZodNumber>;
368
+ image_motion: z.ZodOptional<z.ZodObject<{
369
+ direction: z.ZodEnum<{
370
+ in: "in";
371
+ out: "out";
372
+ }>;
373
+ amount: z.ZodNumber;
374
+ }, z.core.$strip>>;
243
375
  }, z.core.$strip>>>;
376
+ inserts: z.ZodOptional<z.ZodArray<z.ZodObject<{
377
+ id: z.ZodString;
378
+ media_url: z.ZodString;
379
+ media_type: z.ZodEnum<{
380
+ image: "image";
381
+ video: "video";
382
+ }>;
383
+ anchor: z.ZodUnion<readonly [z.ZodObject<{
384
+ startWord: z.ZodNumber;
385
+ endWord: z.ZodNumber;
386
+ }, z.core.$strict>, z.ZodObject<{
387
+ quote: z.ZodString;
388
+ occurrence: z.ZodOptional<z.ZodNumber>;
389
+ }, z.core.$strict>]>;
390
+ cover_words: z.ZodOptional<z.ZodNumber>;
391
+ image_motion: z.ZodOptional<z.ZodObject<{
392
+ direction: z.ZodEnum<{
393
+ in: "in";
394
+ out: "out";
395
+ }>;
396
+ amount: z.ZodNumber;
397
+ }, z.core.$strict>>;
398
+ }, z.core.$strict>>>;
244
399
  person: z.ZodOptional<z.ZodString>;
245
400
  actor_id: z.ZodOptional<z.ZodString>;
246
401
  image: z.ZodOptional<z.ZodString>;
@@ -251,6 +406,11 @@ export declare function serverUgcInputWith(voiceRefusal: (voice: string) => stri
251
406
  character: z.ZodOptional<z.ZodString>;
252
407
  name: z.ZodOptional<z.ZodString>;
253
408
  broll_url: z.ZodOptional<z.ZodString>;
409
+ broll_policy: z.ZodOptional<z.ZodEnum<{
410
+ anyone: "anyone";
411
+ no_actor: "no_actor";
412
+ no_people: "no_people";
413
+ }>>;
254
414
  captions: z.ZodDefault<z.ZodBoolean>;
255
415
  caption_style: z.ZodDefault<z.ZodEnum<{
256
416
  hormozi: "hormozi";
@@ -298,7 +458,42 @@ export declare const storedUgcInput: z.ZodObject<{
298
458
  }>;
299
459
  script: z.ZodOptional<z.ZodString>;
300
460
  media_url: z.ZodOptional<z.ZodString>;
461
+ media_type: z.ZodOptional<z.ZodEnum<{
462
+ image: "image";
463
+ video: "video";
464
+ }>>;
465
+ duration_seconds: z.ZodOptional<z.ZodNumber>;
466
+ image_motion: z.ZodOptional<z.ZodObject<{
467
+ direction: z.ZodEnum<{
468
+ in: "in";
469
+ out: "out";
470
+ }>;
471
+ amount: z.ZodNumber;
472
+ }, z.core.$strip>>;
301
473
  }, z.core.$strip>>>;
474
+ inserts: z.ZodOptional<z.ZodArray<z.ZodObject<{
475
+ id: z.ZodString;
476
+ media_url: z.ZodString;
477
+ media_type: z.ZodEnum<{
478
+ image: "image";
479
+ video: "video";
480
+ }>;
481
+ anchor: z.ZodUnion<readonly [z.ZodObject<{
482
+ startWord: z.ZodNumber;
483
+ endWord: z.ZodNumber;
484
+ }, z.core.$strict>, z.ZodObject<{
485
+ quote: z.ZodString;
486
+ occurrence: z.ZodOptional<z.ZodNumber>;
487
+ }, z.core.$strict>]>;
488
+ cover_words: z.ZodOptional<z.ZodNumber>;
489
+ image_motion: z.ZodOptional<z.ZodObject<{
490
+ direction: z.ZodEnum<{
491
+ in: "in";
492
+ out: "out";
493
+ }>;
494
+ amount: z.ZodNumber;
495
+ }, z.core.$strict>>;
496
+ }, z.core.$strict>>>;
302
497
  person: z.ZodOptional<z.ZodString>;
303
498
  actor_id: z.ZodOptional<z.ZodString>;
304
499
  image: z.ZodOptional<z.ZodString>;
@@ -309,6 +504,11 @@ export declare const storedUgcInput: z.ZodObject<{
309
504
  character: z.ZodOptional<z.ZodString>;
310
505
  name: z.ZodOptional<z.ZodString>;
311
506
  broll_url: z.ZodOptional<z.ZodString>;
507
+ broll_policy: z.ZodOptional<z.ZodEnum<{
508
+ anyone: "anyone";
509
+ no_actor: "no_actor";
510
+ no_people: "no_people";
511
+ }>>;
312
512
  captions: z.ZodDefault<z.ZodBoolean>;
313
513
  caption_style: z.ZodDefault<z.ZodEnum<{
314
514
  hormozi: "hormozi";
@@ -367,6 +567,16 @@ export interface SpokenTextInput {
367
567
  export declare function resolvedScript(input: SpokenTextInput): string;
368
568
  export type MakeUgcInput = z.infer<typeof makeUgcInput>;
369
569
  export declare const quoteResponse: z.ZodObject<{
570
+ long_form_pricing: z.ZodOptional<z.ZodReadonly<z.ZodObject<{
571
+ pricingVersion: z.ZodString;
572
+ rateCreditsPerFaceSecond: z.ZodNumber;
573
+ minimumCredits: z.ZodNumber;
574
+ estimatedTotalDurationSec: z.ZodNumber;
575
+ estimatedFaceSeconds: z.ZodNumber;
576
+ estimatedCredits: z.ZodNumber;
577
+ estimatedFaceWordShare: z.ZodOptional<z.ZodNumber>;
578
+ minimumApplied: z.ZodBoolean;
579
+ }, z.core.$strict>>>;
370
580
  skill: z.ZodString;
371
581
  credits_estimate: z.ZodNumber;
372
582
  duration_estimate_sec: z.ZodNumber;
package/dist/skills.js CHANGED
@@ -1,4 +1,7 @@
1
1
  import { z } from "zod";
2
+ import { longFormQuoteSchema } from "./long-form-pricing.js";
3
+ import { RESOLVED_TIMELINE_MAX_SLOTS } from "./long-form-profile.js";
4
+ import { brollPolicy } from "./broll-policy.js";
2
5
  import { countWords } from "./pacing.js";
3
6
  import { resolveTtsModel, MAX_SCRIPT_CHARS, SCRIPT_LENGTH_DESCRIPTION, STRESS_MARKING_DESCRIPTION, ttsScriptLimitError, TTS_MODELS, isVoicePresetName, VOICE_ID_PATTERN, VOICE_NAME_PATTERN, VOICE_PRESET_NAMES, } from "./voices.js";
4
7
  export const CAPTION_STYLES = ["hormozi", "tiktok", "minimal"];
@@ -67,7 +70,9 @@ export const MAKE_UGC_DESCRIPTION = "Start generation of a lip-synced UGC video.
67
70
  /** поллинг — свойства протокола, и REST-каталогу они не адресованы. */
68
71
  export const MAKE_UGC_AGENT_PROTOCOL = "Call quote_ugc before generating and show the cost. This does NOT wait for the video: it " +
69
72
  "starts the run and returns a run_id IMMEDIATELY. You MUST then poll get_run with that " +
70
- "run_id until the state is 'succeeded' (video_url) or 'failed'. Pass attempt=2,3,… to " +
73
+ "run_id until the state is 'succeeded' (video_url) or 'failed'. A 'failed' run whose paid " +
74
+ "vendor job we still hold can go back to 'queued' and reach 'succeeded' later; whenever that " +
75
+ "happens it is named in warnings[]. Pass attempt=2,3,… to " +
71
76
  "deliberately start a NEW run for the same input (retry after a failure).";
72
77
  /**
73
78
  * Сырой zod-shape входа make_ugc — БЕЗ `.refine()`.
@@ -93,10 +98,57 @@ export const ugcSegment = z.object({
93
98
  script: z.string().min(1).max(MAX_SCRIPT_CHARS).optional(),
94
99
  /** Полнокадровый врез. Обязателен у `media`, запрещён у `actor`. */
95
100
  media_url: z.string().url().max(MAX_URL_LENGTH).optional(),
101
+ media_type: z.enum(["image", "video"]).optional()
102
+ .describe("Explicit media source type; omission means video, regardless of URL suffix. Availability follows the segments field."),
103
+ duration_seconds: z.number().positive().optional()
104
+ .describe("Required for an image segment; its display duration in seconds. Not accepted for actor or video segments."),
105
+ image_motion: z.object({
106
+ direction: z.enum(["in", "out"]),
107
+ amount: z.number().min(0).lt(1),
108
+ }).optional().describe("Optional image zoom: amount is the fractional reduction from fully contained size. No motion by default; no foreground crop."),
109
+ }).superRefine((segment, context) => {
110
+ if (segment.kind === "actor") {
111
+ for (const field of ["media_type", "duration_seconds", "image_motion"]) {
112
+ if (segment[field] !== undefined)
113
+ context.addIssue({ code: z.ZodIssueCode.custom, path: [field], message: `${field} is only accepted on media segments` });
114
+ }
115
+ }
116
+ else if (segment.media_type === "image") {
117
+ if (segment.duration_seconds === undefined)
118
+ context.addIssue({ code: z.ZodIssueCode.custom, path: ["duration_seconds"], message: "image segment requires duration_seconds" });
119
+ }
120
+ else {
121
+ for (const field of ["duration_seconds", "image_motion"]) {
122
+ if (segment[field] !== undefined)
123
+ context.addIssue({ code: z.ZodIssueCode.custom, path: [field], message: `${field} requires media_type=image` });
124
+ }
125
+ }
126
+ });
127
+ export const ugcInsert = z.object({
128
+ id: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/),
129
+ media_url: z.string().url().max(MAX_URL_LENGTH).startsWith("https://"),
130
+ media_type: z.enum(["image", "video"]),
131
+ anchor: z.union([
132
+ z.object({ startWord: z.number().int().nonnegative(), endWord: z.number().int().positive() })
133
+ .strict().refine(range => range.endWord > range.startWord, { message: "endWord must be greater than startWord" }),
134
+ z.object({ quote: z.string().min(1).max(MAX_SCRIPT_CHARS).refine(text => text.trim().length > 0),
135
+ occurrence: z.number().int().nonnegative().optional() }).strict(),
136
+ ]),
137
+ cover_words: z.number().int().positive().max(10000).optional()
138
+ .describe("How many spoken words this insert covers, counted from the first word of its anchor. " +
139
+ "Omitted means the anchored words only. The insert ends where the first uncovered word begins, " +
140
+ "so inserts whose coverage meets are adjacent frame for frame and leave no actor shot between them."),
141
+ image_motion: z.object({ direction: z.enum(["in", "out"]), amount: z.number().min(0).lt(1) }).strict().optional(),
142
+ }).strict().refine(insert => insert.image_motion === undefined || insert.media_type === "image", {
143
+ path: ["image_motion"], message: "image_motion requires media_type=image",
96
144
  });
97
145
  /** Потолок актёрских сегментов — ограничение ОДНОГО рана, не суточных трат. */
98
146
  export const MAX_ACTOR_SEGMENTS = 3;
99
147
  export const MAX_SEGMENTS = 5;
148
+ /** Потолки НАРРАТИВНОГО пути — другие, потому что медиа-шот денег не стоит, */
149
+ /** а актёрский стоит: вставок столько, сколько вмещает таймлайн (ADR #265). */
150
+ export const MAX_INSERTS = Math.floor((RESOLVED_TIMELINE_MAX_SLOTS - 1) / 2);
151
+ export const MAX_NARRATED_ACTOR_SHOTS = 6;
100
152
  export const actorId = z.string().regex(/^actor_[a-z0-9][a-z0-9_-]{0,63}$/);
101
153
  export const makeUgcInputShape = {
102
154
  // НЕОБЯЗАТЕЛЬНЫЙ, но не «необязательный к заполнению»: ровно одно из
@@ -108,10 +160,16 @@ export const makeUgcInputShape = {
108
160
  .min(1, "script is empty: pass the words the actor says")
109
161
  .max(MAX_SCRIPT_CHARS, `script is longer than ${MAX_SCRIPT_CHARS} characters`)
110
162
  .optional()
111
- .describe("required: the words the actor says. The schema leaves it optional for a second way of " +
112
- "passing them that is not accepted yet, so a request without it is refused before any charge. " +
163
+ .describe("The words the actor says; required unless segments supplies the spoken text. " +
164
+ "Segments and text-anchored inserts require long-form qualification on the server. " +
113
165
  `${SCRIPT_LENGTH_DESCRIPTION} ${STRESS_MARKING_DESCRIPTION}`),
114
- segments: z.array(ugcSegment).min(1).max(MAX_SEGMENTS).optional(),
166
+ segments: z.array(ugcSegment).min(1).max(MAX_SEGMENTS).optional()
167
+ .describe("Ordered actor and image segments; requires long-form qualification on the server, captions=false and 1080p. Image media requires explicit broll_policy=anyone."),
168
+ inserts: z.array(ugcInsert).min(1).max(MAX_INSERTS)
169
+ .refine(inserts => new Set(inserts.map(insert => insert.id)).size === inserts.length, {
170
+ message: "insert IDs must be unique",
171
+ }).optional().describe("Text-anchored image inserts over full narration, each covering cover_words spoken words from its anchor; " +
172
+ "requires long-form qualification on the server, captions=false, 1080p and explicit broll_policy=anyone."),
115
173
  person: z.string().min(1).max(500).optional(),
116
174
  actor_id: actorId
117
175
  .optional()
@@ -142,6 +200,7 @@ export const makeUgcInputShape = {
142
200
  character: z.string().regex(/^char_[a-zA-Z0-9]+$/).optional(),
143
201
  name: z.string().max(100).optional(),
144
202
  broll_url: z.string().url().max(MAX_URL_LENGTH).optional(),
203
+ broll_policy: brollPolicy.optional(),
145
204
  captions: z.boolean().default(false),
146
205
  caption_style: z.enum(CAPTION_STYLES).default("hormozi"),
147
206
  look: z.enum(LOOKS).default("natural"),
@@ -231,6 +290,9 @@ export const makeUgcInput = z
231
290
  // молчаливую подмену.
232
291
  .refine((v) => (v.script === undefined) !== (v.segments === undefined), {
233
292
  message: "pass exactly one of script | segments",
293
+ })
294
+ .refine(v => v.inserts === undefined || (v.script !== undefined && v.segments === undefined), {
295
+ path: ["inserts"], message: "inserts requires script and cannot be combined with segments",
234
296
  })
235
297
  // Потолок символов — по модели: v3 короче v2 вдвое; отказ здесь, до резерва и вендора (#101).
236
298
  // Модель незнакомого клиенту голоса знает только сервер, и потолок он проверит сам (#164).
@@ -253,14 +315,8 @@ export const makeUgcInput = z
253
315
  path: ["actor_gender"],
254
316
  message: "actor_gender requires image: it describes the face in your photo; omit it, or pass image",
255
317
  })
256
- // ПРАВИЛА СЕГМЕНТОВ (US-526). Каждое — ОТДЕЛЬНЫЙ .refine, а не одна проверка
257
- // с общим текстом: сообщение обязано называть нарушенное правило, иначе агент
258
- // получает «invalid input» и чинит вход перебором. Порядок правил — от того,
259
- // что делает ролик невозможным, к тому, что делает его противоречивым.
260
- //
261
- // Правила живут ЗДЕСЬ, а не в `ugcSegment`: они про массив целиком (сколько
262
- // актёров) либо про связь двух полей внутри элемента, а `z.object` элемента
263
- // видит только сам элемент.
318
+ // Общие ограничения списка и обязательные поля ролей проверяются здесь.
319
+ // Тип картинки, длительность и движение проверяет ugcSegment.
264
320
  .refine((v) => v.segments === undefined || v.segments.some((s) => s.kind === "actor"), {
265
321
  message: "segments must contain at least one actor segment: nobody speaks otherwise",
266
322
  })
@@ -345,6 +401,7 @@ export function resolvedScript(input) {
345
401
  // из ПРЕДЛАГАЕМОГО shape, а не из полного, иначе автокомплит SDK показывал бы
346
402
  // отклонённые поля, которые сервер отбивает четырёхсотым (блокер B4).
347
403
  export const quoteResponse = z.object({
404
+ long_form_pricing: longFormQuoteSchema.optional(),
348
405
  skill: z.string(),
349
406
  credits_estimate: z.number().int().nonnegative(),
350
407
  duration_estimate_sec: z.number().positive(),