video-editing 1.3.1 → 3.0.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/dist/index.d.ts CHANGED
@@ -1,462 +1,5 @@
1
1
  import { z } from 'zod';
2
2
 
3
- type TokenType = "word" | "spacing" | "audio_event";
4
- /** A single ElevenLabs Scribe token (word-level, verbatim). */
5
- interface ScribeWord {
6
- type?: TokenType;
7
- text?: string;
8
- start: number | null;
9
- end: number | null;
10
- speaker_id?: string | null;
11
- [extra: string]: unknown;
12
- }
13
- interface ScribeTranscript {
14
- language_code?: string;
15
- text?: string;
16
- words: ScribeWord[];
17
- [extra: string]: unknown;
18
- }
19
- /** One KEEP segment in SOURCE seconds. Gaps between ranges are the cuts. */
20
- interface EdlRange {
21
- source: string;
22
- start: number;
23
- end: number;
24
- beat?: string;
25
- quote?: string;
26
- reason?: string;
27
- /** Optional per-range grade override (preset name | raw ffmpeg filter | "auto"). */
28
- grade?: string;
29
- }
30
- interface EdlOverlay {
31
- [k: string]: unknown;
32
- }
33
- interface Edl {
34
- version: number;
35
- sources: Record<string, string>;
36
- /** "none" | preset name | raw ffmpeg filter | "auto" */
37
- grade: string;
38
- ranges: EdlRange[];
39
- overlays: EdlOverlay[];
40
- total_duration_s: number;
41
- /** Optional explicit subtitle path. */
42
- subtitles?: string;
43
- /** Set once the amplitude silence-trim pass has rewritten the ranges. */
44
- desilenced?: boolean;
45
- }
46
- interface SourceJson {
47
- created: string;
48
- source: string;
49
- stem: string;
50
- job_dir: string;
51
- duration_s: number;
52
- stored_width: number;
53
- stored_height: number;
54
- display_width: number;
55
- display_height: number;
56
- rotation: number;
57
- probe: FfprobeResult;
58
- }
59
- interface FfprobeStream {
60
- index?: number;
61
- codec_type?: string;
62
- codec_name?: string;
63
- width?: number;
64
- height?: number;
65
- r_frame_rate?: string;
66
- avg_frame_rate?: string;
67
- duration?: string;
68
- color_transfer?: string;
69
- side_data_list?: Array<Record<string, unknown>>;
70
- tags?: Record<string, string>;
71
- [extra: string]: unknown;
72
- }
73
- interface FfprobeResult {
74
- streams?: FfprobeStream[];
75
- format?: {
76
- duration?: string;
77
- size?: string;
78
- format_name?: string;
79
- [k: string]: unknown;
80
- };
81
- [extra: string]: unknown;
82
- }
83
- interface CutBoundary {
84
- index: number;
85
- output_time_s: number;
86
- removed_source_gap_s: number;
87
- source_before: number;
88
- source_after: number;
89
- }
90
- type RenderMode = "preview" | "draft" | "final";
91
- interface ReviewIssue {
92
- where?: string;
93
- range_index?: number | null;
94
- severity?: "error" | "warning" | "info";
95
- problem?: string;
96
- fix?: string;
97
- }
98
- interface ReviewJson {
99
- verdict?: "clean" | "needs-fixes";
100
- issues?: ReviewIssue[];
101
- confidence?: string;
102
- }
103
- interface Warning {
104
- id: string;
105
- severity: string;
106
- output_time_s: number;
107
- range_index: number | null | undefined;
108
- problem: string;
109
- fix: string;
110
- }
111
- interface VerifyJoinFinding {
112
- index: number;
113
- output_time_s: number;
114
- duplicated_phrase?: string;
115
- gap_s?: number;
116
- severity: "error" | "warning" | "info";
117
- detail: string;
118
- }
119
- interface VerifyResult {
120
- verdict: "clean" | "needs-fixes";
121
- mode: "full" | "joins";
122
- joins: VerifyJoinFinding[];
123
- confidence: string;
124
- }
125
-
126
- interface RunResult {
127
- code: number;
128
- stdout: string;
129
- stderr: string;
130
- }
131
- interface RunOptions {
132
- /** Capture stdout/stderr into strings (default: inherit to the terminal). */
133
- capture?: boolean;
134
- /** Write merged stdout+stderr to this file path. */
135
- stdoutPath?: string;
136
- cwd?: string;
137
- /** Throw on non-zero exit (default true). */
138
- check?: boolean;
139
- /** Echo the command (default true) — mirrors the Python "+ <cmd>" trace. */
140
- echo?: boolean;
141
- }
142
- declare function shown(cmd: string[]): string;
143
- declare class CommandError extends Error {
144
- code: number;
145
- result: RunResult;
146
- constructor(message: string, code: number, result: RunResult);
147
- }
148
- /** Run a command, streaming or capturing output. Async. */
149
- declare function run(cmd: string[], opts?: RunOptions): Promise<RunResult>;
150
- /** Synchronous run (used by a few hot paths / parity tooling). */
151
- declare function runSync(cmd: string[], opts?: {
152
- check?: boolean;
153
- echo?: boolean;
154
- }): RunResult;
155
- /** ffprobe -of json, parsed. Mirrors cli.py ffprobe(). */
156
- declare function ffprobe(path: string): Promise<FfprobeResult>;
157
- declare function videoStream(probe: FfprobeResult): FfprobeStream;
158
- declare function mediaDuration(path: string): Promise<number>;
159
- /** Assert ffmpeg + ffprobe are on PATH. */
160
- declare function requireFfmpeg(): void;
161
-
162
- declare function configDir(): string;
163
- declare function configEnvPath(): string;
164
- interface KeyOptions {
165
- /** explicit --api-key value (highest precedence after env var miss) */
166
- apiKey?: string;
167
- /** extra .env path to check (e.g. for tests) */
168
- envPath?: string;
169
- }
170
- /** Resolve the ElevenLabs API key or throw with a clear message. */
171
- declare function loadApiKey(opts?: KeyOptions): string;
172
-
173
- declare function slugify(value: string): string;
174
- declare function isoNow(): string;
175
- /** Round to `ndigits` using round-half-to-even (Python's round()). */
176
- declare function pyRound(x: number, ndigits?: number): number;
177
- /** Format like Python f"{x:.<prec>f}" (fixed-point, round-half-even-ish). */
178
- declare function pyFixed(x: number, prec: number): string;
179
- /** Python f"{x:0<width>.<prec>f}" — zero-padded fixed width incl. sign/dot. */
180
- declare function pyFixedPad(x: number, width: number, prec: number): string;
181
- /** Python f"{x:<width>.<prec>f}" — space-padded (right-aligned). */
182
- declare function pySpacePad(x: number, width: number, prec: number): string;
183
-
184
- declare function rotationDegrees(stream: FfprobeStream): number;
185
- /** Returns [displayWidth, displayHeight, rotation] accounting for rotation flags. */
186
- declare function displayedDimensions(stream: FfprobeStream): [number, number, number];
187
-
188
- declare const DEFAULT_EDIT_ROOT: string;
189
- declare const VIDEO_EXTS: Set<string>;
190
- declare function defaultJobDir(source: string): string;
191
- declare function transcriptPath(jobDir: string, stem: string): string;
192
- declare function initJob(source: string, jobDir: string | null, opts?: {
193
- force?: boolean;
194
- }): Promise<string>;
195
- declare function loadJob(jobDir: string): SourceJson;
196
- declare function sourceFromJob(jobDir: string): string;
197
- interface ResolvedJob {
198
- jobDir: string;
199
- source: string;
200
- stem: string;
201
- }
202
- /** Resolve a job from a --source or --job-dir argument. */
203
- declare function resolveJob(args: {
204
- source?: string;
205
- jobDir?: string;
206
- }, opts?: {
207
- forceJob?: boolean;
208
- }): Promise<ResolvedJob>;
209
-
210
- declare function ensureVerticalMaster(jobDir: string, source: string, opts?: {
211
- force?: boolean;
212
- }): Promise<string>;
213
-
214
- interface TranscribeOptions {
215
- editDir: string;
216
- language?: string;
217
- numSpeakers?: number;
218
- noHeal?: boolean;
219
- force?: boolean;
220
- apiKey?: string;
221
- verbose?: boolean;
222
- }
223
- /**
224
- * Transcribe a single video. Returns the path to the transcript JSON.
225
- * Cached: returns the existing path immediately if the transcript already
226
- * exists (unless `force`). Mirrors transcribe_one().
227
- */
228
- declare function transcribeOne(video: string, opts: TranscribeOptions): Promise<string>;
229
-
230
- interface BatchOptions {
231
- workers?: number;
232
- language?: string;
233
- numSpeakers?: number;
234
- editDir?: string;
235
- apiKey?: string;
236
- }
237
- interface BatchResult {
238
- videos: string[];
239
- cached: string[];
240
- transcribed: string[];
241
- errors: Array<{
242
- video: string;
243
- message: string;
244
- }>;
245
- }
246
- /**
247
- * Batch transcription. Returns a summary of which files were cached,
248
- * transcribed, and which failed. Mirrors transcribe_batch.main().
249
- */
250
- declare function transcribeBatch(videosDir: string, opts?: BatchOptions): Promise<BatchResult>;
251
-
252
- /**
253
- * Screen a single transcript and return the report text exactly as
254
- * flag_takes.py prints it to stdout (this is what gets written to flags.txt).
255
- */
256
- declare function screenTakes(transcriptPath: string, opts?: {
257
- offset?: number;
258
- longToken?: number;
259
- gap?: number;
260
- }): string;
261
-
262
- interface PackTranscriptsOptions {
263
- silenceThreshold?: number;
264
- output?: string;
265
- }
266
- declare function packTranscripts(editDir: string, opts?: PackTranscriptsOptions): {
267
- path: string;
268
- content: string;
269
- };
270
-
271
- declare const PRESETS: Record<string, string>;
272
- /** Return the ffmpeg filter string for a preset name. Empty string for 'none'.
273
- * Throws (like Python's KeyError) for unknown presets. */
274
- declare function getPreset(name: string): string;
275
- interface GradeStats {
276
- y_mean: number;
277
- y_range: number;
278
- sat_mean: number;
279
- }
280
- /** Pure decision logic — mirrors auto_grade_for_clip's decision rules exactly.
281
- * Bounded to ±8% on any axis, NO color shift. Unit-testable without ffmpeg. */
282
- declare function decideGradeFilter(stats: GradeStats): string;
283
- /** Analyze a clip and return the computed auto-grade `eq=...` filter string
284
- * (or "" for passthrough). Bounded to ±8% on any axis, NO color shift.
285
- *
286
- * Probes the clip duration via ffprobe (matching Python's default behavior),
287
- * samples frames via ffmpeg signalstats, then applies the bounded decision
288
- * rules. Equivalent to Python's auto_grade_for_clip(video)[0]. */
289
- declare function autoGradeForClip(input: string): Promise<string>;
290
-
291
- interface RenderOptions {
292
- preview?: boolean;
293
- draft?: boolean;
294
- buildSubtitles?: boolean;
295
- noSubtitles?: boolean;
296
- noLoudnorm?: boolean;
297
- }
298
- /** Render a video from an EDL. Mirrors render.py main(). */
299
- declare function render(edlPath: string, output: string, opts?: RenderOptions): Promise<void>;
300
-
301
- /**
302
- * Compute a windowed-RMS envelope of length `samples` from raw Int16 PCM.
303
- *
304
- * Pure port of the math in Python's compute_envelope() (after WAV parsing):
305
- * - convert to float32 / 32768
306
- * - window = max(1, floor(n / samples))
307
- * - reshape to (-1, window) over the usable prefix, RMS per window
308
- * - pad with zeros / truncate to `samples`
309
- * - normalize by max so values land in [0, 1]
310
- *
311
- * Returns all-zeros of length `samples` for empty input.
312
- */
313
- declare function computeEnvelope(pcm: Int16Array, samples: number): number[];
314
- interface TimelineViewOptions {
315
- /** Number of frames in the filmstrip (default 10). */
316
- nFrames?: number;
317
- /** Path to a transcript.json for word labels + silence shading. */
318
- transcript?: string;
319
- }
320
- /**
321
- * Render a filmstrip + waveform composite PNG for [start, end] of `video` to
322
- * `output`. Faithful port of render_timeline() in timeline_view.py.
323
- *
324
- * The caller must pass `end < duration`; we clamp `end` to the media duration
325
- * internally (same defensive behavior as the Python EOF guard) to avoid the
326
- * end-window-at-EOF failure.
327
- */
328
- declare function timelineView(video: string, start: number, end: number, output: string, opts?: TimelineViewOptions): Promise<void>;
329
-
330
- interface VerifyOptions {
331
- full?: boolean;
332
- windowS?: number;
333
- gapThresholdS?: number;
334
- apiKey?: string;
335
- }
336
- interface TimedWord {
337
- text: string;
338
- norm: string;
339
- start: number;
340
- end: number;
341
- }
342
- interface DupSpan {
343
- /** The full matched text (abandoned take + keeper). */
344
- text: string;
345
- /** The repeated n-gram length in tokens. 2 = a bare two-word repeat (often
346
- * emphasis / a spelled acronym like "WW" → "דבל יו דבל יו" / a spoken number),
347
- * >= 3 = a restarted clause (the strong retake fingerprint). */
348
- n: number;
349
- }
350
- /**
351
- * ALL near-adjacent repeated phrases (the retake fingerprint), left to right.
352
- * Reports the longest match at each position and skips past it, so a stumbly
353
- * source with several within-window stutters surfaces every one (not just the
354
- * first). Intentional rhetorical repeats ("X? Y X?") also match — the editor
355
- * adjudicates those. Each span carries its n-gram length so callers can weight
356
- * a 3+-word clause restart (almost always a real retake) above a bare 2-word
357
- * repeat (frequently a spelled acronym / number / emphasis, not a retake).
358
- */
359
- declare function duplicatedSpans(words: TimedWord[]): DupSpan[];
360
- /** ALL near-adjacent repeated phrases as plain text (back-compat). */
361
- declare function duplicatedPhrases(words: TimedWord[]): string[];
362
- /** First near-adjacent duplicate (back-compat helper). */
363
- declare function duplicatedPhrase(words: TimedWord[]): string | null;
364
- /** Largest inter-word gap that straddles `joinOffset` (window-relative seconds). */
365
- declare function straddlingGap(words: TimedWord[], joinOffset: number): number;
366
- declare function verify(jobDir: string, opts?: VerifyOptions): Promise<VerifyResult>;
367
-
368
- interface SilenceInterval {
369
- start: number;
370
- end: number;
371
- }
372
- interface DesilenceOptions {
373
- /** Silence threshold in dB (ffmpeg silencedetect `noise`). Default -39. */
374
- noiseDb: number;
375
- /** Minimum silence length (s) to act on — also silencedetect `d`. Default 0.30. */
376
- minSilence: number;
377
- /** Total air (s) to leave where an internal silence is cut. Default 0.13. */
378
- targetGap: number;
379
- /** Air kept BEFORE speech resumes at a cut / leading edge. Default 0.05. */
380
- leadKeep: number;
381
- /** Air kept AFTER speech ends at a cut / trailing edge. Default 0.08. */
382
- trailKeep: number;
383
- /** Drop any resulting sub-range shorter than this (s). Default 0.10. */
384
- minSegment: number;
385
- }
386
- declare const DEFAULT_DESILENCE: DesilenceOptions;
387
- /** ffmpeg argv for an amplitude silence scan. No video, null muxer; the report
388
- * lands on stderr. Pure (no execution). */
389
- declare function silenceDetectArgs(source: string, noiseDb: number, minSilenceS: number): string[];
390
- /** Parse `silence_start:` / `silence_end:` pairs out of ffmpeg stderr.
391
- * A dangling silence_start (silence runs to EOF) is closed at `sourceDurationS`
392
- * when provided (>0), otherwise dropped. */
393
- declare function parseSilenceDetect(stderr: string, sourceDurationS?: number): SilenceInterval[];
394
- /** Tighten ONE keep-range against the detected silences: excise internal
395
- * silences >= minSilence (leaving trailKeep + leadKeep of air) and trim silent
396
- * leading/trailing edges. Returns 1..N sub-ranges, all inside [start,end],
397
- * inheriting source/grade/beat/quote. Pure. */
398
- declare function tightenRange(range: EdlRange, silences: SilenceInterval[], opts?: DesilenceOptions, words?: SilenceInterval[]): EdlRange[];
399
- /** Tighten every range against per-source silence maps, protecting intra-word
400
- * splits with per-source word tokens. Pure. */
401
- declare function tightenRanges(ranges: EdlRange[], silencesBySource: Record<string, SilenceInterval[]>, opts?: DesilenceOptions, wordsBySource?: Record<string, SilenceInterval[]>): EdlRange[];
402
- /** Load word-token spans per EDL source from the job's transcripts/ dir. Tries
403
- * transcripts/<sourceName>.json, then falls back to the lone transcript when a
404
- * single-source job names its transcript by stem (the common case). */
405
- declare function loadWordsBySource(edl: Edl, editDir: string): Record<string, SilenceInterval[]>;
406
- /** Run silencedetect on a source and return the silence intervals. */
407
- declare function detectSilences(source: string, noiseDb: number, minSilenceS: number, sourceDurationS: number): Promise<SilenceInterval[]>;
408
- interface DesilenceStats {
409
- rangesBefore: number;
410
- rangesAfter: number;
411
- durationBefore: number;
412
- durationAfter: number;
413
- removedS: number;
414
- }
415
- /** Detect silences for every source referenced by the EDL, tighten all ranges,
416
- * and return a new EDL (with updated total_duration_s + a `desilenced` marker)
417
- * plus stats. Does NOT write anything. */
418
- declare function desilenceEdl(edl: Edl, editDir: string, opts?: DesilenceOptions): Promise<{
419
- edl: Edl;
420
- stats: DesilenceStats;
421
- }>;
422
-
423
- declare function loadWords(tpath: string, opts?: {
424
- includeEvents?: boolean;
425
- }): ScribeWord[];
426
- declare function writeWordDump(tpath: string, outPath: string, gapThreshold?: number): void;
427
- declare function writeCoverage(jobDir: string, source: string, tpath: string): Promise<string>;
428
- declare function writeTranscriptArtifacts(jobDir: string, source: string, tpath: string): Promise<void>;
429
- declare function ensureTranscribed(jobDir: string, source: string, stem: string, opts?: {
430
- language?: string;
431
- numSpeakers?: number;
432
- noHeal?: boolean;
433
- force?: boolean;
434
- apiKey?: string;
435
- }): Promise<string>;
436
- declare function edlPath(jobDir: string): string;
437
- declare function readEdl(jobDir: string): Edl | null;
438
- declare function cutBoundaries(ranges: Edl["ranges"]): CutBoundary[];
439
- declare function writeBoundaries(jobDir: string): string | null;
440
- declare function generateQcImages(jobDir: string, opts?: {
441
- preview?: string;
442
- maxImages?: number;
443
- }): Promise<string | null>;
444
- declare function writeWarningsFromReview(jobDir: string): string;
445
- declare function artifactPaths(jobDir: string, stem: string): Record<string, string>;
446
- declare function writeAgentManifest(jobDir: string, source: string, stem: string, mode: string): string;
447
- declare function statusPayload(jobDir: string): Promise<Record<string, unknown>>;
448
-
449
- declare function learnedEditingGuardrails(jobDir: string, stem: string): string;
450
- declare function agentizePrompt(text: string, jobDir: string, stem: string): string;
451
- declare function writeVideoEditingPrompt(jobDir: string, stem: string, templateName: string, outName: string): string;
452
- interface SilencePromptOptions {
453
- minGap: number;
454
- targetGap: number;
455
- leadPad: number;
456
- trailPad: number;
457
- }
458
- declare function writeSilencePrompt(jobDir: string, stem: string, opts: SilencePromptOptions): string;
459
-
460
3
  /** Who put an item into the timeline, and in which authoring round. Required on
461
4
  * every item; defaults to {round:null, changeId:null, actor:"agent"}. */
462
5
  declare const provenanceSchema: z.ZodDefault<z.ZodObject<{
@@ -523,14 +66,14 @@ declare const sourceAnchorSchema: z.ZodObject<{
523
66
  end: z.ZodNumber;
524
67
  }, "strict", z.ZodTypeAny, {
525
68
  type: "source";
69
+ source: string;
526
70
  start: number;
527
71
  end: number;
528
- source: string;
529
72
  }, {
530
73
  type: "source";
74
+ source: string;
531
75
  start: number;
532
76
  end: number;
533
- source: string;
534
77
  }>;
535
78
  /** Symbolic OUTPUT-time anchor. Numeric output seconds are deliberately not
536
79
  * representable: output time shifts under desilence/re-cuts, so authored docs
@@ -604,14 +147,14 @@ declare const anchorSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
604
147
  end: z.ZodNumber;
605
148
  }, "strict", z.ZodTypeAny, {
606
149
  type: "source";
150
+ source: string;
607
151
  start: number;
608
152
  end: number;
609
- source: string;
610
153
  }, {
611
154
  type: "source";
155
+ source: string;
612
156
  start: number;
613
157
  end: number;
614
- source: string;
615
158
  }>, z.ZodObject<{
616
159
  type: z.ZodLiteral<"output">;
617
160
  at: z.ZodUnion<[z.ZodEnum<["start", "end", "full"]>, z.ZodObject<{
@@ -655,6 +198,19 @@ declare const timelineSourceSchema: z.ZodObject<{
655
198
  durationS?: number | undefined;
656
199
  transcriptHash?: string | undefined;
657
200
  }>;
201
+ /** Declarative color grade for the composition render path: a named preset
202
+ * with an optional 0..1 intensity. (Distinct from the legacy ffmpeg grade
203
+ * string, which settings.grade also still accepts.) */
204
+ declare const gradeSettingSchema: z.ZodObject<{
205
+ preset: z.ZodString;
206
+ intensity: z.ZodOptional<z.ZodNumber>;
207
+ }, "strict", z.ZodTypeAny, {
208
+ preset: string;
209
+ intensity?: number | undefined;
210
+ }, {
211
+ preset: string;
212
+ intensity?: number | undefined;
213
+ }>;
658
214
  declare const settingsSchema: z.ZodDefault<z.ZodObject<{
659
215
  canvas: z.ZodDefault<z.ZodObject<{
660
216
  w: z.ZodNumber;
@@ -763,9 +319,9 @@ declare const cutRangeSchema: z.ZodObject<{
763
319
  actor?: "agent" | "user" | undefined;
764
320
  }>>;
765
321
  }, "strict", z.ZodTypeAny, {
322
+ source: string;
766
323
  start: number;
767
324
  end: number;
768
- source: string;
769
325
  id: string;
770
326
  provenance: {
771
327
  round: number | null;
@@ -777,9 +333,9 @@ declare const cutRangeSchema: z.ZodObject<{
777
333
  quote?: string | undefined;
778
334
  reason?: string | undefined;
779
335
  }, {
336
+ source: string;
780
337
  start: number;
781
338
  end: number;
782
- source: string;
783
339
  id: string;
784
340
  grade?: string | undefined;
785
341
  beat?: string | undefined;
@@ -853,6 +409,12 @@ declare const captionOverrideSchema: z.ZodObject<{
853
409
  actor?: "agent" | "user" | undefined;
854
410
  }>>;
855
411
  }, "strict", z.ZodTypeAny, {
412
+ id: string;
413
+ provenance: {
414
+ round: number | null;
415
+ changeId: string | null;
416
+ actor: "agent" | "user";
417
+ };
856
418
  anchor: {
857
419
  type: "words";
858
420
  source: string;
@@ -864,17 +426,12 @@ declare const captionOverrideSchema: z.ZodObject<{
864
426
  } | undefined;
865
427
  edge?: "start" | "end" | undefined;
866
428
  };
867
- id: string;
868
- provenance: {
869
- round: number | null;
870
- changeId: string | null;
871
- actor: "agent" | "user";
872
- };
873
429
  op: "replace-text" | "hide" | "break-line" | "style";
874
430
  text?: string | undefined;
875
431
  color?: string | undefined;
876
432
  emphasis?: "pop" | "highlight" | undefined;
877
433
  }, {
434
+ id: string;
878
435
  anchor: {
879
436
  type: "words";
880
437
  source: string;
@@ -886,17 +443,76 @@ declare const captionOverrideSchema: z.ZodObject<{
886
443
  } | undefined;
887
444
  edge?: "start" | "end" | undefined;
888
445
  };
889
- id: string;
890
446
  op: "replace-text" | "hide" | "break-line" | "style";
891
- text?: string | undefined;
892
447
  provenance?: {
893
448
  round?: number | null | undefined;
894
449
  changeId?: string | null | undefined;
895
450
  actor?: "agent" | "user" | undefined;
896
451
  } | undefined;
452
+ text?: string | undefined;
897
453
  color?: string | undefined;
898
454
  emphasis?: "pop" | "highlight" | undefined;
899
455
  }>;
456
+ /** Caption presets:
457
+ * - "grouped" (default): phrases bounded by long inter-word gaps
458
+ * (style.gapBreakS), cut joins and sentence-ending punctuation, packed
459
+ * into balanced cues of up to style.maxWords words (5 words → 3+2, never
460
+ * 4+1; see deriveCaptionCues in compile.ts).
461
+ * - "opus-karaoke": word-by-word (one cue per word), explicit opt-in. */
462
+ declare const captionPresetSchema: z.ZodEnum<["grouped", "opus-karaoke"]>;
463
+ /** Timing/grouping knobs. Renderer-specific style keys pass through untouched;
464
+ * defaults are applied at compile (see CAPTION_STYLE_DEFAULTS) so authored
465
+ * timeline.json files stay minimal. */
466
+ declare const captionStyleSchema: z.ZodObject<{
467
+ /** Max words per grouped cue (preset "grouped" only). */
468
+ maxWords: z.ZodOptional<z.ZodNumber>;
469
+ /** Inter-word gap (seconds) that ends a phrase — a hard cue boundary
470
+ * (preset "grouped" only). */
471
+ gapBreakS: z.ZodOptional<z.ZodNumber>;
472
+ /** Seconds each cue's display start is shifted EARLIER (clamped to the
473
+ * previous cue's end / the segment's outStart). */
474
+ leadS: z.ZodOptional<z.ZodNumber>;
475
+ /** Max seconds a cue is held past its last word to bridge the gap to the
476
+ * next cue (never past the segment's outEnd). */
477
+ holdS: z.ZodOptional<z.ZodNumber>;
478
+ /** Per-word highlight sweep within grouped cues (karaoke-style). */
479
+ karaoke: z.ZodOptional<z.ZodBoolean>;
480
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
481
+ /** Max words per grouped cue (preset "grouped" only). */
482
+ maxWords: z.ZodOptional<z.ZodNumber>;
483
+ /** Inter-word gap (seconds) that ends a phrase — a hard cue boundary
484
+ * (preset "grouped" only). */
485
+ gapBreakS: z.ZodOptional<z.ZodNumber>;
486
+ /** Seconds each cue's display start is shifted EARLIER (clamped to the
487
+ * previous cue's end / the segment's outStart). */
488
+ leadS: z.ZodOptional<z.ZodNumber>;
489
+ /** Max seconds a cue is held past its last word to bridge the gap to the
490
+ * next cue (never past the segment's outEnd). */
491
+ holdS: z.ZodOptional<z.ZodNumber>;
492
+ /** Per-word highlight sweep within grouped cues (karaoke-style). */
493
+ karaoke: z.ZodOptional<z.ZodBoolean>;
494
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
495
+ /** Max words per grouped cue (preset "grouped" only). */
496
+ maxWords: z.ZodOptional<z.ZodNumber>;
497
+ /** Inter-word gap (seconds) that ends a phrase — a hard cue boundary
498
+ * (preset "grouped" only). */
499
+ gapBreakS: z.ZodOptional<z.ZodNumber>;
500
+ /** Seconds each cue's display start is shifted EARLIER (clamped to the
501
+ * previous cue's end / the segment's outStart). */
502
+ leadS: z.ZodOptional<z.ZodNumber>;
503
+ /** Max seconds a cue is held past its last word to bridge the gap to the
504
+ * next cue (never past the segment's outEnd). */
505
+ holdS: z.ZodOptional<z.ZodNumber>;
506
+ /** Per-word highlight sweep within grouped cues (karaoke-style). */
507
+ karaoke: z.ZodOptional<z.ZodBoolean>;
508
+ }, z.ZodTypeAny, "passthrough">>;
509
+ /** Compile-time defaults for the caption style knobs. */
510
+ declare const CAPTION_STYLE_DEFAULTS: {
511
+ readonly maxWords: 4;
512
+ readonly gapBreakS: 0.6;
513
+ readonly leadS: 0.08;
514
+ readonly holdS: 0.9;
515
+ };
900
516
  declare const zoomSchema: z.ZodObject<{
901
517
  id: z.ZodString;
902
518
  anchor: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
@@ -945,14 +561,14 @@ declare const zoomSchema: z.ZodObject<{
945
561
  end: z.ZodNumber;
946
562
  }, "strict", z.ZodTypeAny, {
947
563
  type: "source";
564
+ source: string;
948
565
  start: number;
949
566
  end: number;
950
- source: string;
951
567
  }, {
952
568
  type: "source";
569
+ source: string;
953
570
  start: number;
954
571
  end: number;
955
- source: string;
956
572
  }>, z.ZodObject<{
957
573
  type: z.ZodLiteral<"output">;
958
574
  at: z.ZodUnion<[z.ZodEnum<["start", "end", "full"]>, z.ZodObject<{
@@ -1055,6 +671,12 @@ declare const zoomSchema: z.ZodObject<{
1055
671
  actor?: "agent" | "user" | undefined;
1056
672
  }>>;
1057
673
  }, "strict", z.ZodTypeAny, {
674
+ id: string;
675
+ provenance: {
676
+ round: number | null;
677
+ changeId: string | null;
678
+ actor: "agent" | "user";
679
+ };
1058
680
  anchor: {
1059
681
  type: "words";
1060
682
  source: string;
@@ -1067,9 +689,9 @@ declare const zoomSchema: z.ZodObject<{
1067
689
  edge?: "start" | "end" | undefined;
1068
690
  } | {
1069
691
  type: "source";
692
+ source: string;
1070
693
  start: number;
1071
694
  end: number;
1072
- source: string;
1073
695
  } | {
1074
696
  type: "output";
1075
697
  at: "start" | "end" | "full" | {
@@ -1077,13 +699,7 @@ declare const zoomSchema: z.ZodObject<{
1077
699
  };
1078
700
  durationS?: number | undefined;
1079
701
  };
1080
- id: string;
1081
- provenance: {
1082
- round: number | null;
1083
- changeId: string | null;
1084
- actor: "agent" | "user";
1085
- };
1086
- onCut: "error" | "drop" | "clip";
702
+ onCut: "clip" | "drop" | "error";
1087
703
  rect: {
1088
704
  w: number;
1089
705
  h: number;
@@ -1105,6 +721,7 @@ declare const zoomSchema: z.ZodObject<{
1105
721
  scale?: number | undefined;
1106
722
  } | undefined;
1107
723
  }, {
724
+ id: string;
1108
725
  anchor: {
1109
726
  type: "words";
1110
727
  source: string;
@@ -1117,9 +734,9 @@ declare const zoomSchema: z.ZodObject<{
1117
734
  edge?: "start" | "end" | undefined;
1118
735
  } | {
1119
736
  type: "source";
737
+ source: string;
1120
738
  start: number;
1121
739
  end: number;
1122
- source: string;
1123
740
  } | {
1124
741
  type: "output";
1125
742
  at: "start" | "end" | "full" | {
@@ -1127,7 +744,6 @@ declare const zoomSchema: z.ZodObject<{
1127
744
  };
1128
745
  durationS?: number | undefined;
1129
746
  };
1130
- id: string;
1131
747
  rect: {
1132
748
  w: number;
1133
749
  h: number;
@@ -1140,7 +756,7 @@ declare const zoomSchema: z.ZodObject<{
1140
756
  changeId?: string | null | undefined;
1141
757
  actor?: "agent" | "user" | undefined;
1142
758
  } | undefined;
1143
- onCut?: "error" | "drop" | "clip" | undefined;
759
+ onCut?: "clip" | "drop" | "error" | undefined;
1144
760
  to?: {
1145
761
  rect?: {
1146
762
  w: number;
@@ -1161,11 +777,11 @@ declare const overlayFxSchema: z.ZodObject<{
1161
777
  preset: z.ZodEnum<["fade", "pop", "slide-up", "slide-down", "scale-in"]>;
1162
778
  durationS: z.ZodNumber;
1163
779
  }, "strict", z.ZodTypeAny, {
1164
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1165
780
  durationS: number;
1166
- }, {
1167
781
  preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
782
+ }, {
1168
783
  durationS: number;
784
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1169
785
  }>;
1170
786
  declare const overlaySchema: z.ZodObject<{
1171
787
  id: z.ZodString;
@@ -1216,14 +832,14 @@ declare const overlaySchema: z.ZodObject<{
1216
832
  end: z.ZodNumber;
1217
833
  }, "strict", z.ZodTypeAny, {
1218
834
  type: "source";
835
+ source: string;
1219
836
  start: number;
1220
837
  end: number;
1221
- source: string;
1222
838
  }, {
1223
839
  type: "source";
840
+ source: string;
1224
841
  start: number;
1225
842
  end: number;
1226
- source: string;
1227
843
  }>, z.ZodObject<{
1228
844
  type: z.ZodLiteral<"output">;
1229
845
  at: z.ZodUnion<[z.ZodEnum<["start", "end", "full"]>, z.ZodObject<{
@@ -1256,12 +872,12 @@ declare const overlaySchema: z.ZodObject<{
1256
872
  }, "strict", z.ZodTypeAny, {
1257
873
  x: number;
1258
874
  y: number;
1259
- align: "center" | "left" | "right";
875
+ align: "left" | "center" | "right";
1260
876
  safeArea: boolean;
1261
877
  }, {
1262
878
  x?: number | undefined;
1263
879
  y?: number | undefined;
1264
- align?: "center" | "left" | "right" | undefined;
880
+ align?: "left" | "center" | "right" | undefined;
1265
881
  safeArea?: boolean | undefined;
1266
882
  }>>;
1267
883
  text: z.ZodOptional<z.ZodString>;
@@ -1285,21 +901,21 @@ declare const overlaySchema: z.ZodObject<{
1285
901
  preset: z.ZodEnum<["fade", "pop", "slide-up", "slide-down", "scale-in"]>;
1286
902
  durationS: z.ZodNumber;
1287
903
  }, "strict", z.ZodTypeAny, {
1288
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1289
904
  durationS: number;
1290
- }, {
1291
905
  preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
906
+ }, {
1292
907
  durationS: number;
908
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1293
909
  }>>;
1294
910
  exit: z.ZodOptional<z.ZodObject<{
1295
911
  preset: z.ZodEnum<["fade", "pop", "slide-up", "slide-down", "scale-in"]>;
1296
912
  durationS: z.ZodNumber;
1297
913
  }, "strict", z.ZodTypeAny, {
1298
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1299
914
  durationS: number;
1300
- }, {
1301
915
  preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
916
+ }, {
1302
917
  durationS: number;
918
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1303
919
  }>>;
1304
920
  /** Size as canvas fractions (w of canvas width, h of canvas height). */
1305
921
  size: z.ZodOptional<z.ZodObject<{
@@ -1328,6 +944,12 @@ declare const overlaySchema: z.ZodObject<{
1328
944
  actor?: "agent" | "user" | undefined;
1329
945
  }>>;
1330
946
  }, "strict", z.ZodTypeAny, {
947
+ id: string;
948
+ provenance: {
949
+ round: number | null;
950
+ changeId: string | null;
951
+ actor: "agent" | "user";
952
+ };
1331
953
  anchor: {
1332
954
  type: "words";
1333
955
  source: string;
@@ -1340,9 +962,9 @@ declare const overlaySchema: z.ZodObject<{
1340
962
  edge?: "start" | "end" | undefined;
1341
963
  } | {
1342
964
  type: "source";
965
+ source: string;
1343
966
  start: number;
1344
967
  end: number;
1345
- source: string;
1346
968
  } | {
1347
969
  type: "output";
1348
970
  at: "start" | "end" | "full" | {
@@ -1350,44 +972,39 @@ declare const overlaySchema: z.ZodObject<{
1350
972
  };
1351
973
  durationS?: number | undefined;
1352
974
  };
1353
- kind: "text" | "title-card" | "image" | "sticker";
1354
- id: string;
1355
- provenance: {
1356
- round: number | null;
1357
- changeId: string | null;
1358
- actor: "agent" | "user";
1359
- };
1360
975
  style: {
1361
976
  preset?: string | undefined;
1362
977
  } & {
1363
978
  [k: string]: unknown;
1364
979
  };
1365
- onCut: "error" | "drop" | "clip";
980
+ onCut: "clip" | "drop" | "error";
981
+ kind: "text" | "title-card" | "image" | "sticker";
1366
982
  layout: {
1367
983
  x: number;
1368
984
  y: number;
1369
- align: "center" | "left" | "right";
985
+ align: "left" | "center" | "right";
1370
986
  safeArea: boolean;
1371
987
  };
1372
- text?: string | undefined;
1373
- size?: {
1374
- w?: number | undefined;
1375
- h?: number | undefined;
1376
- } | undefined;
1377
988
  durationS?: number | undefined;
989
+ text?: string | undefined;
1378
990
  asset?: {
1379
991
  ref: string;
1380
992
  } | undefined;
1381
993
  enter?: {
1382
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1383
994
  durationS: number;
995
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1384
996
  } | undefined;
1385
997
  exit?: {
1386
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1387
998
  durationS: number;
999
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1000
+ } | undefined;
1001
+ size?: {
1002
+ w?: number | undefined;
1003
+ h?: number | undefined;
1388
1004
  } | undefined;
1389
1005
  z?: number | undefined;
1390
1006
  }, {
1007
+ id: string;
1391
1008
  anchor: {
1392
1009
  type: "words";
1393
1010
  source: string;
@@ -1400,9 +1017,9 @@ declare const overlaySchema: z.ZodObject<{
1400
1017
  edge?: "start" | "end" | undefined;
1401
1018
  } | {
1402
1019
  type: "source";
1020
+ source: string;
1403
1021
  start: number;
1404
1022
  end: number;
1405
- source: string;
1406
1023
  } | {
1407
1024
  type: "output";
1408
1025
  at: "start" | "end" | "full" | {
@@ -1411,12 +1028,6 @@ declare const overlaySchema: z.ZodObject<{
1411
1028
  durationS?: number | undefined;
1412
1029
  };
1413
1030
  kind: "text" | "title-card" | "image" | "sticker";
1414
- id: string;
1415
- text?: string | undefined;
1416
- size?: {
1417
- w?: number | undefined;
1418
- h?: number | undefined;
1419
- } | undefined;
1420
1031
  durationS?: number | undefined;
1421
1032
  provenance?: {
1422
1033
  round?: number | null | undefined;
@@ -1426,25 +1037,30 @@ declare const overlaySchema: z.ZodObject<{
1426
1037
  style?: z.objectInputType<{
1427
1038
  preset: z.ZodOptional<z.ZodString>;
1428
1039
  }, z.ZodTypeAny, "passthrough"> | undefined;
1429
- onCut?: "error" | "drop" | "clip" | undefined;
1040
+ text?: string | undefined;
1041
+ onCut?: "clip" | "drop" | "error" | undefined;
1430
1042
  layout?: {
1431
1043
  x?: number | undefined;
1432
1044
  y?: number | undefined;
1433
- align?: "center" | "left" | "right" | undefined;
1045
+ align?: "left" | "center" | "right" | undefined;
1434
1046
  safeArea?: boolean | undefined;
1435
1047
  } | undefined;
1436
1048
  asset?: {
1437
1049
  ref: string;
1438
1050
  } | undefined;
1439
1051
  enter?: {
1440
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1441
1052
  durationS: number;
1053
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1442
1054
  } | undefined;
1443
1055
  exit?: {
1444
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1445
1056
  durationS: number;
1057
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
1446
1058
  } | undefined;
1447
- z?: number | undefined;
1059
+ size?: {
1060
+ w?: number | undefined;
1061
+ h?: number | undefined;
1062
+ } | undefined;
1063
+ z?: number | undefined;
1448
1064
  }>;
1449
1065
  declare const audioSchema: z.ZodObject<{
1450
1066
  id: z.ZodString;
@@ -1502,14 +1118,14 @@ declare const audioSchema: z.ZodObject<{
1502
1118
  end: z.ZodNumber;
1503
1119
  }, "strict", z.ZodTypeAny, {
1504
1120
  type: "source";
1121
+ source: string;
1505
1122
  start: number;
1506
1123
  end: number;
1507
- source: string;
1508
1124
  }, {
1509
1125
  type: "source";
1126
+ source: string;
1510
1127
  start: number;
1511
1128
  end: number;
1512
- source: string;
1513
1129
  }>, z.ZodObject<{
1514
1130
  type: z.ZodLiteral<"output">;
1515
1131
  at: z.ZodUnion<[z.ZodEnum<["start", "end", "full"]>, z.ZodObject<{
@@ -1550,6 +1166,12 @@ declare const audioSchema: z.ZodObject<{
1550
1166
  actor?: "agent" | "user" | undefined;
1551
1167
  }>>;
1552
1168
  }, "strict", z.ZodTypeAny, {
1169
+ id: string;
1170
+ provenance: {
1171
+ round: number | null;
1172
+ changeId: string | null;
1173
+ actor: "agent" | "user";
1174
+ };
1553
1175
  anchor: {
1554
1176
  type: "words";
1555
1177
  source: string;
@@ -1562,9 +1184,9 @@ declare const audioSchema: z.ZodObject<{
1562
1184
  edge?: "start" | "end" | undefined;
1563
1185
  } | {
1564
1186
  type: "source";
1187
+ source: string;
1565
1188
  start: number;
1566
1189
  end: number;
1567
- source: string;
1568
1190
  } | {
1569
1191
  type: "output";
1570
1192
  at: "start" | "end" | "full" | {
@@ -1573,12 +1195,6 @@ declare const audioSchema: z.ZodObject<{
1573
1195
  durationS?: number | undefined;
1574
1196
  };
1575
1197
  kind: "music" | "sfx";
1576
- id: string;
1577
- provenance: {
1578
- round: number | null;
1579
- changeId: string | null;
1580
- actor: "agent" | "user";
1581
- };
1582
1198
  asset: {
1583
1199
  ref: string;
1584
1200
  };
@@ -1586,6 +1202,7 @@ declare const audioSchema: z.ZodObject<{
1586
1202
  duckToSpeech: boolean;
1587
1203
  fadeOutS?: number | undefined;
1588
1204
  }, {
1205
+ id: string;
1589
1206
  anchor: {
1590
1207
  type: "words";
1591
1208
  source: string;
@@ -1598,9 +1215,9 @@ declare const audioSchema: z.ZodObject<{
1598
1215
  edge?: "start" | "end" | undefined;
1599
1216
  } | {
1600
1217
  type: "source";
1218
+ source: string;
1601
1219
  start: number;
1602
1220
  end: number;
1603
- source: string;
1604
1221
  } | {
1605
1222
  type: "output";
1606
1223
  at: "start" | "end" | "full" | {
@@ -1609,7 +1226,6 @@ declare const audioSchema: z.ZodObject<{
1609
1226
  durationS?: number | undefined;
1610
1227
  };
1611
1228
  kind: "music" | "sfx";
1612
- id: string;
1613
1229
  asset: {
1614
1230
  ref: string;
1615
1231
  };
@@ -1623,10 +1239,13 @@ declare const audioSchema: z.ZodObject<{
1623
1239
  fadeOutS?: number | undefined;
1624
1240
  }>;
1625
1241
  type CutRange = z.infer<typeof cutRangeSchema>;
1242
+ type CaptionPreset = z.infer<typeof captionPresetSchema>;
1243
+ type CaptionStyle = z.infer<typeof captionStyleSchema>;
1626
1244
  type CaptionOverride = z.infer<typeof captionOverrideSchema>;
1627
1245
  type ZoomItem = z.infer<typeof zoomSchema>;
1628
1246
  type OverlayItem = z.infer<typeof overlaySchema>;
1629
1247
  type OverlayFx = z.infer<typeof overlayFxSchema>;
1248
+ type GradeSetting = z.infer<typeof gradeSettingSchema>;
1630
1249
  type AudioItem = z.infer<typeof audioSchema>;
1631
1250
  type TimelineSource = z.infer<typeof timelineSourceSchema>;
1632
1251
  type TimelineSettings = z.infer<typeof settingsSchema>;
@@ -1766,9 +1385,9 @@ declare const timelineV2Schema: z.ZodObject<{
1766
1385
  actor?: "agent" | "user" | undefined;
1767
1386
  }>>;
1768
1387
  }, "strict", z.ZodTypeAny, {
1388
+ source: string;
1769
1389
  start: number;
1770
1390
  end: number;
1771
- source: string;
1772
1391
  id: string;
1773
1392
  provenance: {
1774
1393
  round: number | null;
@@ -1780,9 +1399,9 @@ declare const timelineV2Schema: z.ZodObject<{
1780
1399
  quote?: string | undefined;
1781
1400
  reason?: string | undefined;
1782
1401
  }, {
1402
+ source: string;
1783
1403
  start: number;
1784
1404
  end: number;
1785
- source: string;
1786
1405
  id: string;
1787
1406
  grade?: string | undefined;
1788
1407
  beat?: string | undefined;
@@ -1796,9 +1415,9 @@ declare const timelineV2Schema: z.ZodObject<{
1796
1415
  }>, "many">>;
1797
1416
  }, "strict", z.ZodTypeAny, {
1798
1417
  ranges: {
1418
+ source: string;
1799
1419
  start: number;
1800
1420
  end: number;
1801
- source: string;
1802
1421
  id: string;
1803
1422
  provenance: {
1804
1423
  round: number | null;
@@ -1812,9 +1431,9 @@ declare const timelineV2Schema: z.ZodObject<{
1812
1431
  }[];
1813
1432
  }, {
1814
1433
  ranges?: {
1434
+ source: string;
1815
1435
  start: number;
1816
1436
  end: number;
1817
- source: string;
1818
1437
  id: string;
1819
1438
  grade?: string | undefined;
1820
1439
  beat?: string | undefined;
@@ -1935,6 +1554,12 @@ declare const timelineV2Schema: z.ZodObject<{
1935
1554
  actor?: "agent" | "user" | undefined;
1936
1555
  }>>;
1937
1556
  }, "strict", z.ZodTypeAny, {
1557
+ id: string;
1558
+ provenance: {
1559
+ round: number | null;
1560
+ changeId: string | null;
1561
+ actor: "agent" | "user";
1562
+ };
1938
1563
  anchor: {
1939
1564
  type: "words";
1940
1565
  source: string;
@@ -1946,17 +1571,12 @@ declare const timelineV2Schema: z.ZodObject<{
1946
1571
  } | undefined;
1947
1572
  edge?: "start" | "end" | undefined;
1948
1573
  };
1949
- id: string;
1950
- provenance: {
1951
- round: number | null;
1952
- changeId: string | null;
1953
- actor: "agent" | "user";
1954
- };
1955
1574
  op: "replace-text" | "hide" | "break-line" | "style";
1956
1575
  text?: string | undefined;
1957
1576
  color?: string | undefined;
1958
1577
  emphasis?: "pop" | "highlight" | undefined;
1959
1578
  }, {
1579
+ id: string;
1960
1580
  anchor: {
1961
1581
  type: "words";
1962
1582
  source: string;
@@ -1968,14 +1588,13 @@ declare const timelineV2Schema: z.ZodObject<{
1968
1588
  } | undefined;
1969
1589
  edge?: "start" | "end" | undefined;
1970
1590
  };
1971
- id: string;
1972
1591
  op: "replace-text" | "hide" | "break-line" | "style";
1973
- text?: string | undefined;
1974
1592
  provenance?: {
1975
1593
  round?: number | null | undefined;
1976
1594
  changeId?: string | null | undefined;
1977
1595
  actor?: "agent" | "user" | undefined;
1978
1596
  } | undefined;
1597
+ text?: string | undefined;
1979
1598
  color?: string | undefined;
1980
1599
  emphasis?: "pop" | "highlight" | undefined;
1981
1600
  }>, "many">>;
@@ -1992,6 +1611,12 @@ declare const timelineV2Schema: z.ZodObject<{
1992
1611
  };
1993
1612
  enabled: boolean;
1994
1613
  overrides: {
1614
+ id: string;
1615
+ provenance: {
1616
+ round: number | null;
1617
+ changeId: string | null;
1618
+ actor: "agent" | "user";
1619
+ };
1995
1620
  anchor: {
1996
1621
  type: "words";
1997
1622
  source: string;
@@ -2003,12 +1628,6 @@ declare const timelineV2Schema: z.ZodObject<{
2003
1628
  } | undefined;
2004
1629
  edge?: "start" | "end" | undefined;
2005
1630
  };
2006
- id: string;
2007
- provenance: {
2008
- round: number | null;
2009
- changeId: string | null;
2010
- actor: "agent" | "user";
2011
- };
2012
1631
  op: "replace-text" | "hide" | "break-line" | "style";
2013
1632
  text?: string | undefined;
2014
1633
  color?: string | undefined;
@@ -2033,6 +1652,7 @@ declare const timelineV2Schema: z.ZodObject<{
2033
1652
  }, z.ZodTypeAny, "passthrough"> | undefined;
2034
1653
  enabled?: boolean | undefined;
2035
1654
  overrides?: {
1655
+ id: string;
2036
1656
  anchor: {
2037
1657
  type: "words";
2038
1658
  source: string;
@@ -2044,14 +1664,13 @@ declare const timelineV2Schema: z.ZodObject<{
2044
1664
  } | undefined;
2045
1665
  edge?: "start" | "end" | undefined;
2046
1666
  };
2047
- id: string;
2048
1667
  op: "replace-text" | "hide" | "break-line" | "style";
2049
- text?: string | undefined;
2050
1668
  provenance?: {
2051
1669
  round?: number | null | undefined;
2052
1670
  changeId?: string | null | undefined;
2053
1671
  actor?: "agent" | "user" | undefined;
2054
1672
  } | undefined;
1673
+ text?: string | undefined;
2055
1674
  color?: string | undefined;
2056
1675
  emphasis?: "pop" | "highlight" | undefined;
2057
1676
  }[] | undefined;
@@ -2104,14 +1723,14 @@ declare const timelineV2Schema: z.ZodObject<{
2104
1723
  end: z.ZodNumber;
2105
1724
  }, "strict", z.ZodTypeAny, {
2106
1725
  type: "source";
1726
+ source: string;
2107
1727
  start: number;
2108
1728
  end: number;
2109
- source: string;
2110
1729
  }, {
2111
1730
  type: "source";
1731
+ source: string;
2112
1732
  start: number;
2113
1733
  end: number;
2114
- source: string;
2115
1734
  }>, z.ZodObject<{
2116
1735
  type: z.ZodLiteral<"output">;
2117
1736
  at: z.ZodUnion<[z.ZodEnum<["start", "end", "full"]>, z.ZodObject<{
@@ -2214,6 +1833,12 @@ declare const timelineV2Schema: z.ZodObject<{
2214
1833
  actor?: "agent" | "user" | undefined;
2215
1834
  }>>;
2216
1835
  }, "strict", z.ZodTypeAny, {
1836
+ id: string;
1837
+ provenance: {
1838
+ round: number | null;
1839
+ changeId: string | null;
1840
+ actor: "agent" | "user";
1841
+ };
2217
1842
  anchor: {
2218
1843
  type: "words";
2219
1844
  source: string;
@@ -2226,9 +1851,9 @@ declare const timelineV2Schema: z.ZodObject<{
2226
1851
  edge?: "start" | "end" | undefined;
2227
1852
  } | {
2228
1853
  type: "source";
1854
+ source: string;
2229
1855
  start: number;
2230
1856
  end: number;
2231
- source: string;
2232
1857
  } | {
2233
1858
  type: "output";
2234
1859
  at: "start" | "end" | "full" | {
@@ -2236,13 +1861,7 @@ declare const timelineV2Schema: z.ZodObject<{
2236
1861
  };
2237
1862
  durationS?: number | undefined;
2238
1863
  };
2239
- id: string;
2240
- provenance: {
2241
- round: number | null;
2242
- changeId: string | null;
2243
- actor: "agent" | "user";
2244
- };
2245
- onCut: "error" | "drop" | "clip";
1864
+ onCut: "clip" | "drop" | "error";
2246
1865
  rect: {
2247
1866
  w: number;
2248
1867
  h: number;
@@ -2264,6 +1883,7 @@ declare const timelineV2Schema: z.ZodObject<{
2264
1883
  scale?: number | undefined;
2265
1884
  } | undefined;
2266
1885
  }, {
1886
+ id: string;
2267
1887
  anchor: {
2268
1888
  type: "words";
2269
1889
  source: string;
@@ -2276,9 +1896,9 @@ declare const timelineV2Schema: z.ZodObject<{
2276
1896
  edge?: "start" | "end" | undefined;
2277
1897
  } | {
2278
1898
  type: "source";
1899
+ source: string;
2279
1900
  start: number;
2280
1901
  end: number;
2281
- source: string;
2282
1902
  } | {
2283
1903
  type: "output";
2284
1904
  at: "start" | "end" | "full" | {
@@ -2286,7 +1906,6 @@ declare const timelineV2Schema: z.ZodObject<{
2286
1906
  };
2287
1907
  durationS?: number | undefined;
2288
1908
  };
2289
- id: string;
2290
1909
  rect: {
2291
1910
  w: number;
2292
1911
  h: number;
@@ -2299,7 +1918,7 @@ declare const timelineV2Schema: z.ZodObject<{
2299
1918
  changeId?: string | null | undefined;
2300
1919
  actor?: "agent" | "user" | undefined;
2301
1920
  } | undefined;
2302
- onCut?: "error" | "drop" | "clip" | undefined;
1921
+ onCut?: "clip" | "drop" | "error" | undefined;
2303
1922
  to?: {
2304
1923
  rect?: {
2305
1924
  w: number;
@@ -2363,14 +1982,14 @@ declare const timelineV2Schema: z.ZodObject<{
2363
1982
  end: z.ZodNumber;
2364
1983
  }, "strict", z.ZodTypeAny, {
2365
1984
  type: "source";
1985
+ source: string;
2366
1986
  start: number;
2367
1987
  end: number;
2368
- source: string;
2369
1988
  }, {
2370
1989
  type: "source";
1990
+ source: string;
2371
1991
  start: number;
2372
1992
  end: number;
2373
- source: string;
2374
1993
  }>, z.ZodObject<{
2375
1994
  type: z.ZodLiteral<"output">;
2376
1995
  at: z.ZodUnion<[z.ZodEnum<["start", "end", "full"]>, z.ZodObject<{
@@ -2403,12 +2022,12 @@ declare const timelineV2Schema: z.ZodObject<{
2403
2022
  }, "strict", z.ZodTypeAny, {
2404
2023
  x: number;
2405
2024
  y: number;
2406
- align: "center" | "left" | "right";
2025
+ align: "left" | "center" | "right";
2407
2026
  safeArea: boolean;
2408
2027
  }, {
2409
2028
  x?: number | undefined;
2410
2029
  y?: number | undefined;
2411
- align?: "center" | "left" | "right" | undefined;
2030
+ align?: "left" | "center" | "right" | undefined;
2412
2031
  safeArea?: boolean | undefined;
2413
2032
  }>>;
2414
2033
  text: z.ZodOptional<z.ZodString>;
@@ -2432,21 +2051,21 @@ declare const timelineV2Schema: z.ZodObject<{
2432
2051
  preset: z.ZodEnum<["fade", "pop", "slide-up", "slide-down", "scale-in"]>;
2433
2052
  durationS: z.ZodNumber;
2434
2053
  }, "strict", z.ZodTypeAny, {
2435
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2436
2054
  durationS: number;
2437
- }, {
2438
2055
  preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2056
+ }, {
2439
2057
  durationS: number;
2058
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2440
2059
  }>>;
2441
2060
  exit: z.ZodOptional<z.ZodObject<{
2442
2061
  preset: z.ZodEnum<["fade", "pop", "slide-up", "slide-down", "scale-in"]>;
2443
2062
  durationS: z.ZodNumber;
2444
2063
  }, "strict", z.ZodTypeAny, {
2445
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2446
2064
  durationS: number;
2447
- }, {
2448
2065
  preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2066
+ }, {
2449
2067
  durationS: number;
2068
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2450
2069
  }>>;
2451
2070
  /** Size as canvas fractions (w of canvas width, h of canvas height). */
2452
2071
  size: z.ZodOptional<z.ZodObject<{
@@ -2475,6 +2094,12 @@ declare const timelineV2Schema: z.ZodObject<{
2475
2094
  actor?: "agent" | "user" | undefined;
2476
2095
  }>>;
2477
2096
  }, "strict", z.ZodTypeAny, {
2097
+ id: string;
2098
+ provenance: {
2099
+ round: number | null;
2100
+ changeId: string | null;
2101
+ actor: "agent" | "user";
2102
+ };
2478
2103
  anchor: {
2479
2104
  type: "words";
2480
2105
  source: string;
@@ -2487,9 +2112,9 @@ declare const timelineV2Schema: z.ZodObject<{
2487
2112
  edge?: "start" | "end" | undefined;
2488
2113
  } | {
2489
2114
  type: "source";
2115
+ source: string;
2490
2116
  start: number;
2491
2117
  end: number;
2492
- source: string;
2493
2118
  } | {
2494
2119
  type: "output";
2495
2120
  at: "start" | "end" | "full" | {
@@ -2497,44 +2122,39 @@ declare const timelineV2Schema: z.ZodObject<{
2497
2122
  };
2498
2123
  durationS?: number | undefined;
2499
2124
  };
2500
- kind: "text" | "title-card" | "image" | "sticker";
2501
- id: string;
2502
- provenance: {
2503
- round: number | null;
2504
- changeId: string | null;
2505
- actor: "agent" | "user";
2506
- };
2507
2125
  style: {
2508
2126
  preset?: string | undefined;
2509
2127
  } & {
2510
2128
  [k: string]: unknown;
2511
2129
  };
2512
- onCut: "error" | "drop" | "clip";
2130
+ onCut: "clip" | "drop" | "error";
2131
+ kind: "text" | "title-card" | "image" | "sticker";
2513
2132
  layout: {
2514
2133
  x: number;
2515
2134
  y: number;
2516
- align: "center" | "left" | "right";
2135
+ align: "left" | "center" | "right";
2517
2136
  safeArea: boolean;
2518
2137
  };
2519
- text?: string | undefined;
2520
- size?: {
2521
- w?: number | undefined;
2522
- h?: number | undefined;
2523
- } | undefined;
2524
2138
  durationS?: number | undefined;
2139
+ text?: string | undefined;
2525
2140
  asset?: {
2526
2141
  ref: string;
2527
2142
  } | undefined;
2528
2143
  enter?: {
2529
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2530
2144
  durationS: number;
2145
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2531
2146
  } | undefined;
2532
2147
  exit?: {
2533
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2534
2148
  durationS: number;
2149
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2150
+ } | undefined;
2151
+ size?: {
2152
+ w?: number | undefined;
2153
+ h?: number | undefined;
2535
2154
  } | undefined;
2536
2155
  z?: number | undefined;
2537
2156
  }, {
2157
+ id: string;
2538
2158
  anchor: {
2539
2159
  type: "words";
2540
2160
  source: string;
@@ -2547,9 +2167,9 @@ declare const timelineV2Schema: z.ZodObject<{
2547
2167
  edge?: "start" | "end" | undefined;
2548
2168
  } | {
2549
2169
  type: "source";
2170
+ source: string;
2550
2171
  start: number;
2551
2172
  end: number;
2552
- source: string;
2553
2173
  } | {
2554
2174
  type: "output";
2555
2175
  at: "start" | "end" | "full" | {
@@ -2558,12 +2178,6 @@ declare const timelineV2Schema: z.ZodObject<{
2558
2178
  durationS?: number | undefined;
2559
2179
  };
2560
2180
  kind: "text" | "title-card" | "image" | "sticker";
2561
- id: string;
2562
- text?: string | undefined;
2563
- size?: {
2564
- w?: number | undefined;
2565
- h?: number | undefined;
2566
- } | undefined;
2567
2181
  durationS?: number | undefined;
2568
2182
  provenance?: {
2569
2183
  round?: number | null | undefined;
@@ -2573,23 +2187,28 @@ declare const timelineV2Schema: z.ZodObject<{
2573
2187
  style?: z.objectInputType<{
2574
2188
  preset: z.ZodOptional<z.ZodString>;
2575
2189
  }, z.ZodTypeAny, "passthrough"> | undefined;
2576
- onCut?: "error" | "drop" | "clip" | undefined;
2190
+ text?: string | undefined;
2191
+ onCut?: "clip" | "drop" | "error" | undefined;
2577
2192
  layout?: {
2578
2193
  x?: number | undefined;
2579
2194
  y?: number | undefined;
2580
- align?: "center" | "left" | "right" | undefined;
2195
+ align?: "left" | "center" | "right" | undefined;
2581
2196
  safeArea?: boolean | undefined;
2582
2197
  } | undefined;
2583
2198
  asset?: {
2584
2199
  ref: string;
2585
2200
  } | undefined;
2586
2201
  enter?: {
2587
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2588
2202
  durationS: number;
2203
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2589
2204
  } | undefined;
2590
2205
  exit?: {
2591
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2592
2206
  durationS: number;
2207
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2208
+ } | undefined;
2209
+ size?: {
2210
+ w?: number | undefined;
2211
+ h?: number | undefined;
2593
2212
  } | undefined;
2594
2213
  z?: number | undefined;
2595
2214
  }>, "many">>;
@@ -2649,14 +2268,14 @@ declare const timelineV2Schema: z.ZodObject<{
2649
2268
  end: z.ZodNumber;
2650
2269
  }, "strict", z.ZodTypeAny, {
2651
2270
  type: "source";
2271
+ source: string;
2652
2272
  start: number;
2653
2273
  end: number;
2654
- source: string;
2655
2274
  }, {
2656
2275
  type: "source";
2276
+ source: string;
2657
2277
  start: number;
2658
2278
  end: number;
2659
- source: string;
2660
2279
  }>, z.ZodObject<{
2661
2280
  type: z.ZodLiteral<"output">;
2662
2281
  at: z.ZodUnion<[z.ZodEnum<["start", "end", "full"]>, z.ZodObject<{
@@ -2697,6 +2316,12 @@ declare const timelineV2Schema: z.ZodObject<{
2697
2316
  actor?: "agent" | "user" | undefined;
2698
2317
  }>>;
2699
2318
  }, "strict", z.ZodTypeAny, {
2319
+ id: string;
2320
+ provenance: {
2321
+ round: number | null;
2322
+ changeId: string | null;
2323
+ actor: "agent" | "user";
2324
+ };
2700
2325
  anchor: {
2701
2326
  type: "words";
2702
2327
  source: string;
@@ -2709,9 +2334,9 @@ declare const timelineV2Schema: z.ZodObject<{
2709
2334
  edge?: "start" | "end" | undefined;
2710
2335
  } | {
2711
2336
  type: "source";
2337
+ source: string;
2712
2338
  start: number;
2713
2339
  end: number;
2714
- source: string;
2715
2340
  } | {
2716
2341
  type: "output";
2717
2342
  at: "start" | "end" | "full" | {
@@ -2720,12 +2345,6 @@ declare const timelineV2Schema: z.ZodObject<{
2720
2345
  durationS?: number | undefined;
2721
2346
  };
2722
2347
  kind: "music" | "sfx";
2723
- id: string;
2724
- provenance: {
2725
- round: number | null;
2726
- changeId: string | null;
2727
- actor: "agent" | "user";
2728
- };
2729
2348
  asset: {
2730
2349
  ref: string;
2731
2350
  };
@@ -2733,6 +2352,7 @@ declare const timelineV2Schema: z.ZodObject<{
2733
2352
  duckToSpeech: boolean;
2734
2353
  fadeOutS?: number | undefined;
2735
2354
  }, {
2355
+ id: string;
2736
2356
  anchor: {
2737
2357
  type: "words";
2738
2358
  source: string;
@@ -2745,9 +2365,9 @@ declare const timelineV2Schema: z.ZodObject<{
2745
2365
  edge?: "start" | "end" | undefined;
2746
2366
  } | {
2747
2367
  type: "source";
2368
+ source: string;
2748
2369
  start: number;
2749
2370
  end: number;
2750
- source: string;
2751
2371
  } | {
2752
2372
  type: "output";
2753
2373
  at: "start" | "end" | "full" | {
@@ -2756,7 +2376,6 @@ declare const timelineV2Schema: z.ZodObject<{
2756
2376
  durationS?: number | undefined;
2757
2377
  };
2758
2378
  kind: "music" | "sfx";
2759
- id: string;
2760
2379
  asset: {
2761
2380
  ref: string;
2762
2381
  };
@@ -2770,43 +2389,6 @@ declare const timelineV2Schema: z.ZodObject<{
2770
2389
  fadeOutS?: number | undefined;
2771
2390
  }>, "many">>;
2772
2391
  }, "strict", z.ZodTypeAny, {
2773
- audio: {
2774
- anchor: {
2775
- type: "words";
2776
- source: string;
2777
- phrase: string;
2778
- occurrence?: number | undefined;
2779
- resolved?: {
2780
- start: number;
2781
- end: number;
2782
- } | undefined;
2783
- edge?: "start" | "end" | undefined;
2784
- } | {
2785
- type: "source";
2786
- start: number;
2787
- end: number;
2788
- source: string;
2789
- } | {
2790
- type: "output";
2791
- at: "start" | "end" | "full" | {
2792
- after: string;
2793
- };
2794
- durationS?: number | undefined;
2795
- };
2796
- kind: "music" | "sfx";
2797
- id: string;
2798
- provenance: {
2799
- round: number | null;
2800
- changeId: string | null;
2801
- actor: "agent" | "user";
2802
- };
2803
- asset: {
2804
- ref: string;
2805
- };
2806
- gainDb: number;
2807
- duckToSpeech: boolean;
2808
- fadeOutS?: number | undefined;
2809
- }[];
2810
2392
  id: string;
2811
2393
  version: 2;
2812
2394
  sources: Record<string, {
@@ -2835,9 +2417,9 @@ declare const timelineV2Schema: z.ZodObject<{
2835
2417
  };
2836
2418
  cut: {
2837
2419
  ranges: {
2420
+ source: string;
2838
2421
  start: number;
2839
2422
  end: number;
2840
- source: string;
2841
2423
  id: string;
2842
2424
  provenance: {
2843
2425
  round: number | null;
@@ -2863,6 +2445,12 @@ declare const timelineV2Schema: z.ZodObject<{
2863
2445
  };
2864
2446
  enabled: boolean;
2865
2447
  overrides: {
2448
+ id: string;
2449
+ provenance: {
2450
+ round: number | null;
2451
+ changeId: string | null;
2452
+ actor: "agent" | "user";
2453
+ };
2866
2454
  anchor: {
2867
2455
  type: "words";
2868
2456
  source: string;
@@ -2874,12 +2462,6 @@ declare const timelineV2Schema: z.ZodObject<{
2874
2462
  } | undefined;
2875
2463
  edge?: "start" | "end" | undefined;
2876
2464
  };
2877
- id: string;
2878
- provenance: {
2879
- round: number | null;
2880
- changeId: string | null;
2881
- actor: "agent" | "user";
2882
- };
2883
2465
  op: "replace-text" | "hide" | "break-line" | "style";
2884
2466
  text?: string | undefined;
2885
2467
  color?: string | undefined;
@@ -2887,6 +2469,12 @@ declare const timelineV2Schema: z.ZodObject<{
2887
2469
  }[];
2888
2470
  };
2889
2471
  zooms: {
2472
+ id: string;
2473
+ provenance: {
2474
+ round: number | null;
2475
+ changeId: string | null;
2476
+ actor: "agent" | "user";
2477
+ };
2890
2478
  anchor: {
2891
2479
  type: "words";
2892
2480
  source: string;
@@ -2899,9 +2487,9 @@ declare const timelineV2Schema: z.ZodObject<{
2899
2487
  edge?: "start" | "end" | undefined;
2900
2488
  } | {
2901
2489
  type: "source";
2490
+ source: string;
2902
2491
  start: number;
2903
2492
  end: number;
2904
- source: string;
2905
2493
  } | {
2906
2494
  type: "output";
2907
2495
  at: "start" | "end" | "full" | {
@@ -2909,13 +2497,7 @@ declare const timelineV2Schema: z.ZodObject<{
2909
2497
  };
2910
2498
  durationS?: number | undefined;
2911
2499
  };
2912
- id: string;
2913
- provenance: {
2914
- round: number | null;
2915
- changeId: string | null;
2916
- actor: "agent" | "user";
2917
- };
2918
- onCut: "error" | "drop" | "clip";
2500
+ onCut: "clip" | "drop" | "error";
2919
2501
  rect: {
2920
2502
  w: number;
2921
2503
  h: number;
@@ -2938,6 +2520,12 @@ declare const timelineV2Schema: z.ZodObject<{
2938
2520
  } | undefined;
2939
2521
  }[];
2940
2522
  overlays: {
2523
+ id: string;
2524
+ provenance: {
2525
+ round: number | null;
2526
+ changeId: string | null;
2527
+ actor: "agent" | "user";
2528
+ };
2941
2529
  anchor: {
2942
2530
  type: "words";
2943
2531
  source: string;
@@ -2950,9 +2538,9 @@ declare const timelineV2Schema: z.ZodObject<{
2950
2538
  edge?: "start" | "end" | undefined;
2951
2539
  } | {
2952
2540
  type: "source";
2541
+ source: string;
2953
2542
  start: number;
2954
2543
  end: number;
2955
- source: string;
2956
2544
  } | {
2957
2545
  type: "output";
2958
2546
  at: "start" | "end" | "full" | {
@@ -2960,53 +2548,45 @@ declare const timelineV2Schema: z.ZodObject<{
2960
2548
  };
2961
2549
  durationS?: number | undefined;
2962
2550
  };
2963
- kind: "text" | "title-card" | "image" | "sticker";
2964
- id: string;
2965
- provenance: {
2966
- round: number | null;
2967
- changeId: string | null;
2968
- actor: "agent" | "user";
2969
- };
2970
2551
  style: {
2971
2552
  preset?: string | undefined;
2972
2553
  } & {
2973
2554
  [k: string]: unknown;
2974
2555
  };
2975
- onCut: "error" | "drop" | "clip";
2556
+ onCut: "clip" | "drop" | "error";
2557
+ kind: "text" | "title-card" | "image" | "sticker";
2976
2558
  layout: {
2977
2559
  x: number;
2978
2560
  y: number;
2979
- align: "center" | "left" | "right";
2561
+ align: "left" | "center" | "right";
2980
2562
  safeArea: boolean;
2981
2563
  };
2564
+ durationS?: number | undefined;
2982
2565
  text?: string | undefined;
2983
- size?: {
2984
- w?: number | undefined;
2985
- h?: number | undefined;
2986
- } | undefined;
2987
- durationS?: number | undefined;
2988
2566
  asset?: {
2989
2567
  ref: string;
2990
2568
  } | undefined;
2991
2569
  enter?: {
2992
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2993
2570
  durationS: number;
2571
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2994
2572
  } | undefined;
2995
2573
  exit?: {
2996
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2997
2574
  durationS: number;
2575
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2576
+ } | undefined;
2577
+ size?: {
2578
+ w?: number | undefined;
2579
+ h?: number | undefined;
2998
2580
  } | undefined;
2999
2581
  z?: number | undefined;
3000
2582
  }[];
3001
- }, {
3002
- id: string;
3003
- version: 2;
3004
- sources: Record<string, {
3005
- path: string;
3006
- durationS?: number | undefined;
3007
- transcriptHash?: string | undefined;
3008
- }>;
3009
- audio?: {
2583
+ audio: {
2584
+ id: string;
2585
+ provenance: {
2586
+ round: number | null;
2587
+ changeId: string | null;
2588
+ actor: "agent" | "user";
2589
+ };
3010
2590
  anchor: {
3011
2591
  type: "words";
3012
2592
  source: string;
@@ -3019,9 +2599,9 @@ declare const timelineV2Schema: z.ZodObject<{
3019
2599
  edge?: "start" | "end" | undefined;
3020
2600
  } | {
3021
2601
  type: "source";
2602
+ source: string;
3022
2603
  start: number;
3023
2604
  end: number;
3024
- source: string;
3025
2605
  } | {
3026
2606
  type: "output";
3027
2607
  at: "start" | "end" | "full" | {
@@ -3030,19 +2610,21 @@ declare const timelineV2Schema: z.ZodObject<{
3030
2610
  durationS?: number | undefined;
3031
2611
  };
3032
2612
  kind: "music" | "sfx";
3033
- id: string;
3034
2613
  asset: {
3035
2614
  ref: string;
3036
2615
  };
3037
- provenance?: {
3038
- round?: number | null | undefined;
3039
- changeId?: string | null | undefined;
3040
- actor?: "agent" | "user" | undefined;
3041
- } | undefined;
3042
- gainDb?: number | undefined;
3043
- duckToSpeech?: boolean | undefined;
2616
+ gainDb: number;
2617
+ duckToSpeech: boolean;
3044
2618
  fadeOutS?: number | undefined;
3045
- }[] | undefined;
2619
+ }[];
2620
+ }, {
2621
+ id: string;
2622
+ version: 2;
2623
+ sources: Record<string, {
2624
+ path: string;
2625
+ durationS?: number | undefined;
2626
+ transcriptHash?: string | undefined;
2627
+ }>;
3046
2628
  settings?: {
3047
2629
  canvas?: {
3048
2630
  w: number;
@@ -3064,9 +2646,9 @@ declare const timelineV2Schema: z.ZodObject<{
3064
2646
  } | undefined;
3065
2647
  cut?: {
3066
2648
  ranges?: {
2649
+ source: string;
3067
2650
  start: number;
3068
2651
  end: number;
3069
- source: string;
3070
2652
  id: string;
3071
2653
  grade?: string | undefined;
3072
2654
  beat?: string | undefined;
@@ -3098,6 +2680,7 @@ declare const timelineV2Schema: z.ZodObject<{
3098
2680
  }, z.ZodTypeAny, "passthrough"> | undefined;
3099
2681
  enabled?: boolean | undefined;
3100
2682
  overrides?: {
2683
+ id: string;
3101
2684
  anchor: {
3102
2685
  type: "words";
3103
2686
  source: string;
@@ -3109,19 +2692,19 @@ declare const timelineV2Schema: z.ZodObject<{
3109
2692
  } | undefined;
3110
2693
  edge?: "start" | "end" | undefined;
3111
2694
  };
3112
- id: string;
3113
2695
  op: "replace-text" | "hide" | "break-line" | "style";
3114
- text?: string | undefined;
3115
2696
  provenance?: {
3116
2697
  round?: number | null | undefined;
3117
2698
  changeId?: string | null | undefined;
3118
2699
  actor?: "agent" | "user" | undefined;
3119
2700
  } | undefined;
2701
+ text?: string | undefined;
3120
2702
  color?: string | undefined;
3121
2703
  emphasis?: "pop" | "highlight" | undefined;
3122
2704
  }[] | undefined;
3123
2705
  } | undefined;
3124
2706
  zooms?: {
2707
+ id: string;
3125
2708
  anchor: {
3126
2709
  type: "words";
3127
2710
  source: string;
@@ -3134,9 +2717,9 @@ declare const timelineV2Schema: z.ZodObject<{
3134
2717
  edge?: "start" | "end" | undefined;
3135
2718
  } | {
3136
2719
  type: "source";
2720
+ source: string;
3137
2721
  start: number;
3138
2722
  end: number;
3139
- source: string;
3140
2723
  } | {
3141
2724
  type: "output";
3142
2725
  at: "start" | "end" | "full" | {
@@ -3144,7 +2727,6 @@ declare const timelineV2Schema: z.ZodObject<{
3144
2727
  };
3145
2728
  durationS?: number | undefined;
3146
2729
  };
3147
- id: string;
3148
2730
  rect: {
3149
2731
  w: number;
3150
2732
  h: number;
@@ -3157,7 +2739,7 @@ declare const timelineV2Schema: z.ZodObject<{
3157
2739
  changeId?: string | null | undefined;
3158
2740
  actor?: "agent" | "user" | undefined;
3159
2741
  } | undefined;
3160
- onCut?: "error" | "drop" | "clip" | undefined;
2742
+ onCut?: "clip" | "drop" | "error" | undefined;
3161
2743
  to?: {
3162
2744
  rect?: {
3163
2745
  w: number;
@@ -3173,6 +2755,7 @@ declare const timelineV2Schema: z.ZodObject<{
3173
2755
  } | undefined;
3174
2756
  }[] | undefined;
3175
2757
  overlays?: {
2758
+ id: string;
3176
2759
  anchor: {
3177
2760
  type: "words";
3178
2761
  source: string;
@@ -3185,9 +2768,9 @@ declare const timelineV2Schema: z.ZodObject<{
3185
2768
  edge?: "start" | "end" | undefined;
3186
2769
  } | {
3187
2770
  type: "source";
2771
+ source: string;
3188
2772
  start: number;
3189
2773
  end: number;
3190
- source: string;
3191
2774
  } | {
3192
2775
  type: "output";
3193
2776
  at: "start" | "end" | "full" | {
@@ -3196,12 +2779,6 @@ declare const timelineV2Schema: z.ZodObject<{
3196
2779
  durationS?: number | undefined;
3197
2780
  };
3198
2781
  kind: "text" | "title-card" | "image" | "sticker";
3199
- id: string;
3200
- text?: string | undefined;
3201
- size?: {
3202
- w?: number | undefined;
3203
- h?: number | undefined;
3204
- } | undefined;
3205
2782
  durationS?: number | undefined;
3206
2783
  provenance?: {
3207
2784
  round?: number | null | undefined;
@@ -3211,26 +2788,68 @@ declare const timelineV2Schema: z.ZodObject<{
3211
2788
  style?: z.objectInputType<{
3212
2789
  preset: z.ZodOptional<z.ZodString>;
3213
2790
  }, z.ZodTypeAny, "passthrough"> | undefined;
3214
- onCut?: "error" | "drop" | "clip" | undefined;
2791
+ text?: string | undefined;
2792
+ onCut?: "clip" | "drop" | "error" | undefined;
3215
2793
  layout?: {
3216
2794
  x?: number | undefined;
3217
2795
  y?: number | undefined;
3218
- align?: "center" | "left" | "right" | undefined;
2796
+ align?: "left" | "center" | "right" | undefined;
3219
2797
  safeArea?: boolean | undefined;
3220
2798
  } | undefined;
3221
2799
  asset?: {
3222
2800
  ref: string;
3223
2801
  } | undefined;
3224
2802
  enter?: {
3225
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
3226
2803
  durationS: number;
2804
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
3227
2805
  } | undefined;
3228
2806
  exit?: {
3229
- preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
3230
2807
  durationS: number;
2808
+ preset: "pop" | "fade" | "slide-up" | "slide-down" | "scale-in";
2809
+ } | undefined;
2810
+ size?: {
2811
+ w?: number | undefined;
2812
+ h?: number | undefined;
3231
2813
  } | undefined;
3232
2814
  z?: number | undefined;
3233
2815
  }[] | undefined;
2816
+ audio?: {
2817
+ id: string;
2818
+ anchor: {
2819
+ type: "words";
2820
+ source: string;
2821
+ phrase: string;
2822
+ occurrence?: number | undefined;
2823
+ resolved?: {
2824
+ start: number;
2825
+ end: number;
2826
+ } | undefined;
2827
+ edge?: "start" | "end" | undefined;
2828
+ } | {
2829
+ type: "source";
2830
+ source: string;
2831
+ start: number;
2832
+ end: number;
2833
+ } | {
2834
+ type: "output";
2835
+ at: "start" | "end" | "full" | {
2836
+ after: string;
2837
+ };
2838
+ durationS?: number | undefined;
2839
+ };
2840
+ kind: "music" | "sfx";
2841
+ asset: {
2842
+ ref: string;
2843
+ };
2844
+ provenance?: {
2845
+ round?: number | null | undefined;
2846
+ changeId?: string | null | undefined;
2847
+ actor?: "agent" | "user" | undefined;
2848
+ } | undefined;
2849
+ gainDb?: number | undefined;
2850
+ duckToSpeech?: boolean | undefined;
2851
+ fadeOutS?: number | undefined;
2852
+ }[] | undefined;
3234
2853
  }>;
3235
2854
  type TimelineV2 = z.infer<typeof timelineV2Schema>;
3236
2855
  interface Diagnostic {
@@ -3247,6 +2866,7 @@ interface Diagnostics {
3247
2866
  /** Item ids removed by onCut:"drop" policy during compile. */
3248
2867
  dropped: string[];
3249
2868
  }
2869
+ declare function emptyDiagnostics(): Diagnostics;
3250
2870
  interface ParseResult {
3251
2871
  doc: TimelineV2 | null;
3252
2872
  errors: Diagnostic[];
@@ -3276,6 +2896,71 @@ declare function checkCapabilities(uses: string[], registry?: Record<string, Cap
3276
2896
  warnings: Diagnostic[];
3277
2897
  };
3278
2898
 
2899
+ type TokenType = "word" | "spacing" | "audio_event";
2900
+ /** A single ElevenLabs Scribe token (word-level, verbatim). */
2901
+ interface ScribeWord {
2902
+ type?: TokenType;
2903
+ text?: string;
2904
+ start: number | null;
2905
+ end: number | null;
2906
+ speaker_id?: string | null;
2907
+ [extra: string]: unknown;
2908
+ }
2909
+ interface ScribeTranscript {
2910
+ language_code?: string;
2911
+ text?: string;
2912
+ words: ScribeWord[];
2913
+ [extra: string]: unknown;
2914
+ }
2915
+ /** One KEEP segment in SOURCE seconds. Gaps between ranges are the cuts. */
2916
+ interface EdlRange {
2917
+ source: string;
2918
+ start: number;
2919
+ end: number;
2920
+ beat?: string;
2921
+ quote?: string;
2922
+ reason?: string;
2923
+ /** Optional per-range grade override (preset name | raw ffmpeg filter | "auto"). */
2924
+ grade?: string;
2925
+ }
2926
+ interface EdlOverlay {
2927
+ [k: string]: unknown;
2928
+ }
2929
+ interface Edl {
2930
+ version: number;
2931
+ sources: Record<string, string>;
2932
+ /** "none" | preset name | raw ffmpeg filter | "auto" */
2933
+ grade: string;
2934
+ ranges: EdlRange[];
2935
+ overlays: EdlOverlay[];
2936
+ total_duration_s: number;
2937
+ /** Optional explicit subtitle path. */
2938
+ subtitles?: string;
2939
+ /** Set once the amplitude silence-trim pass has rewritten the ranges. */
2940
+ desilenced?: boolean;
2941
+ }
2942
+ interface CutBoundary {
2943
+ index: number;
2944
+ output_time_s: number;
2945
+ removed_source_gap_s: number;
2946
+ source_before: number;
2947
+ source_after: number;
2948
+ }
2949
+ interface VerifyJoinFinding {
2950
+ index: number;
2951
+ output_time_s: number;
2952
+ duplicated_phrase?: string;
2953
+ gap_s?: number;
2954
+ severity: "error" | "warning" | "info";
2955
+ detail: string;
2956
+ }
2957
+ interface VerifyResult {
2958
+ verdict: "clean" | "needs-fixes";
2959
+ mode: "full" | "joins";
2960
+ joins: VerifyJoinFinding[];
2961
+ confidence: string;
2962
+ }
2963
+
3279
2964
  interface StreamWord {
3280
2965
  /** Index in the word stream (word tokens only, sorted by start). */
3281
2966
  index: number;
@@ -3341,229 +3026,721 @@ interface ResolveOptions {
3341
3026
  * fuzzy suggestions. */
3342
3027
  declare function resolvePhrase(stream: StreamWord[], phrase: string, opts?: ResolveOptions): ResolveOutcome;
3343
3028
 
3344
- interface PlanZoomOp {
3345
- kind: "zoom";
3346
- itemId: string;
3347
- rect: {
3348
- x: number;
3349
- y: number;
3350
- w: number;
3351
- h: number;
3352
- };
3353
- scale: number;
3354
- ease: {
3355
- in: number;
3356
- out: number;
3357
- };
3358
- /** Seconds relative to the segment's own start. */
3359
- tIn: number;
3360
- tOut: number;
3361
- /** Ramp target (zoom `to`): animate rect/scale base → target over [tIn, tOut]. */
3362
- toRect?: {
3363
- x: number;
3364
- y: number;
3365
- w: number;
3366
- h: number;
3367
- };
3368
- toScale?: number;
3029
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
3030
+ declare const DEFAULT_TIMEOUT_MS = 30000;
3031
+ declare const DEFAULT_DOWNLOAD_TIMEOUT_MS = 300000;
3032
+ /** base = --api-base flag → CLIPREADY_API_BASE → API_BASE → the hosted cloud. */
3033
+ declare function resolveApiBase(flag: string | undefined, env?: NodeJS.ProcessEnv): string;
3034
+ /** credential = --api-key flag → CLIPREADY_API_KEY → RUN_TOKEN. */
3035
+ declare function resolveApiKey(flag: string | undefined, env?: NodeJS.ProcessEnv): string | undefined;
3036
+ /** video id = --video-id flag → VIDEO_ID. */
3037
+ declare function resolveVideoId(flag: string | undefined, env?: NodeJS.ProcessEnv): string | undefined;
3038
+ interface CloudFlags {
3039
+ apiBase?: string;
3040
+ apiKey?: string;
3041
+ videoId?: string;
3369
3042
  }
3370
- type PlanVideoOp = PlanZoomOp;
3371
- interface PlanSegment {
3372
- id: string;
3373
- source: string;
3374
- srcStart: number;
3375
- srcEnd: number;
3376
- outStart: number;
3377
- outEnd: number;
3378
- /** The authored cut range this segment came from (pre-desilence). */
3379
- rangeId: string;
3380
- videoOps: PlanVideoOp[];
3043
+ interface ResolvedConfig {
3044
+ base: string;
3045
+ credential: string;
3046
+ videoId?: string;
3381
3047
  }
3382
- interface PlanCueWord {
3383
- /** Offset from the cue's outStart. */
3048
+ declare function stripTrailingSlash(base: string): string;
3049
+ /** Resolve base + credential (always required) and video id (required only when
3050
+ * `requireVideoId`). Throws a clear, actionable Error the CLI turns into exit 1. */
3051
+ declare function resolveConfig(flags: CloudFlags, opts?: {
3052
+ requireVideoId?: boolean;
3053
+ }, env?: NodeJS.ProcessEnv): ResolvedConfig;
3054
+ declare function rendersUrl(base: string): string;
3055
+ declare function renderUrl(base: string, renderId: string): string;
3056
+ declare function reviewUrl(base: string, videoId: string): string;
3057
+ declare function assetsUrl(base: string): string;
3058
+ declare function meUrl(base: string): string;
3059
+ declare function projectsUrl(base: string): string;
3060
+ declare function projectVideosUrl(base: string, projectId: string): string;
3061
+ declare function runUrl(base: string, runId: string): string;
3062
+ /** Chunked-upload session routes: /api/v1/uploads/multipart[/parts|complete|abort|relay|relay-complete]. */
3063
+ declare function multipartUrl(base: string, tail?: "parts" | "complete" | "abort" | "relay" | "relay-complete"): string;
3064
+ /** Authenticated relay POST for one raw sub-chunk of a multipart part
3065
+ * (i is the 1-based sub-chunk index, of the sub-chunk count). */
3066
+ declare function multipartRelayUrl(base: string, uploadId: string, n: number, i: number, of: number): string;
3067
+ declare function filesUrl(base: string, videoId: string, path?: string): string;
3068
+ declare function transcribeUrl(base: string, videoId: string): string;
3069
+ /** POST /videos/{id}/ingest — server-side probe + audio extraction +
3070
+ * transcription + screening over the uploaded source (202 {run_id}). */
3071
+ declare function ingestUrl(base: string, videoId: string): string;
3072
+ declare function compileUrl(base: string, videoId: string): string;
3073
+ declare function resolveUrl(base: string, videoId: string): string;
3074
+ declare function verifyUrl(base: string, videoId: string): string;
3075
+ declare function composeUrl(base: string, videoId: string): string;
3076
+ type RenderMode = "preview" | "final";
3077
+ type RenderStatus = "pending" | "running" | "completed" | "failed";
3078
+ interface RenderCreate {
3079
+ renderId: string;
3080
+ status: RenderStatus;
3081
+ }
3082
+ interface RenderState {
3083
+ status: RenderStatus;
3084
+ videoUrl?: string;
3085
+ error?: unknown;
3086
+ }
3087
+ declare function parseRenderCreate(data: unknown): RenderCreate;
3088
+ declare function parseRenderState(data: unknown): RenderState;
3089
+ declare function isTerminalStatus(s: RenderStatus): boolean;
3090
+ interface ArtifactContent {
3091
+ name: string;
3092
+ content: unknown;
3093
+ }
3094
+ /** POST /api/v1/assets response (clipready assets route): the created asset
3095
+ * row id plus a presigned URL the caller PUTs the bytes to directly. */
3096
+ interface AssetCreate {
3097
+ assetId: string;
3098
+ uploadUrl: string;
3099
+ }
3100
+ interface QaFramesResult {
3101
+ url: string;
3102
+ width: number;
3103
+ height: number;
3104
+ frames: number;
3105
+ tStart: number;
3106
+ tEnd: number;
3107
+ }
3108
+ interface QaWaveWord {
3384
3109
  t: number;
3385
- d: number;
3110
+ end: number;
3386
3111
  text: string;
3387
- /** Per-word color (op:"style" caption override). */
3388
- color?: string;
3389
- /** Per-word animated emphasis at the word's onset (op:"style" override). */
3390
- emphasis?: "pop" | "highlight";
3391
3112
  }
3392
- interface PlanCaptionCueEvent {
3393
- kind: "caption-cue";
3394
- outStart: number;
3395
- outEnd: number;
3396
- words: PlanCueWord[];
3397
- /** Force a line break after this cue (break-line override). */
3398
- lineBreakAfter?: boolean;
3399
- /** Set when the cue came from a caption override. */
3400
- itemId?: string;
3401
- /** Per-word highlight sweep within the cue (captions.style.karaoke). */
3402
- karaoke?: boolean;
3113
+ interface QaWaveformResult {
3114
+ url: string;
3115
+ words: QaWaveWord[];
3403
3116
  }
3404
- interface PlanOverlayEvent {
3405
- kind: "overlay";
3406
- itemId: string;
3407
- outStart: number;
3408
- outEnd: number;
3409
- overlay: {
3410
- kind: OverlayItem["kind"];
3411
- layout: OverlayItem["layout"];
3412
- text?: string;
3413
- style: OverlayItem["style"];
3414
- asset?: {
3415
- ref: string;
3416
- };
3417
- enter?: OverlayFx;
3418
- exit?: OverlayFx;
3419
- size?: {
3420
- w?: number;
3421
- h?: number;
3422
- };
3423
- z?: number;
3424
- };
3117
+ /** POST /videos/{id}/qa {kind:"inspect"} → the combined filmstrip + waveform
3118
+ * composite with the plan's cut regions shaded. */
3119
+ interface QaInspectResult {
3120
+ url: string;
3121
+ tStart: number;
3122
+ tEnd: number;
3123
+ cuts: Array<{
3124
+ start: number;
3125
+ end: number;
3126
+ }>;
3127
+ cached: boolean;
3425
3128
  }
3426
- interface PlanAudioEvent {
3427
- kind: "audio";
3428
- itemId: string;
3429
- outStart: number;
3430
- outEnd: number;
3431
- audio: {
3432
- kind: AudioItem["kind"];
3433
- asset: {
3434
- ref: string;
3435
- };
3436
- gainDb: number;
3437
- duckToSpeech: boolean;
3438
- fadeOutS?: number;
3439
- };
3129
+ /** One row of the VFS listing. */
3130
+ interface VfsFileEntry {
3131
+ path: string;
3132
+ backend: string;
3133
+ content_type: string | null;
3134
+ bytes: number | null;
3135
+ sha256: string | null;
3136
+ status: string | null;
3137
+ updated_at: string | null;
3138
+ updated_by: string | null;
3440
3139
  }
3441
- type PlanEvent = PlanCaptionCueEvent | PlanOverlayEvent | PlanAudioEvent;
3442
- interface Plan {
3443
- version: 1;
3444
- fromTimeline: string;
3445
- timelineHash: string;
3446
- outputDurationS: number;
3447
- segments: PlanSegment[];
3448
- events: PlanEvent[];
3449
- uses: string[];
3450
- boundaries: CutBoundary[];
3140
+ declare function parseFileList(data: unknown): VfsFileEntry[];
3141
+ /** GET ?path= result: db-backed files carry inline `content`, bucket-backed
3142
+ * files carry a signed `download_url`. Exactly one of the two is set. */
3143
+ interface VfsFileContent {
3144
+ path: string;
3145
+ content?: string;
3146
+ downloadUrl?: string;
3147
+ sha256: string | null;
3148
+ contentType: string | null;
3451
3149
  }
3452
- interface CompileCtx {
3453
- /** Parsed word-level Scribe transcripts by source name. */
3454
- transcripts?: Record<string, ScribeTranscript | undefined>;
3455
- /** "sha256:…" hash of each source's transcript file (anchor cache key). */
3456
- transcriptHashes?: Record<string, string>;
3457
- /** Amplitude silences by source (from ffmpeg silencedetect). When omitted,
3458
- * the desilence pass sees no silences and keeps ranges as authored. */
3459
- silencesBySource?: Record<string, SilenceInterval[]>;
3460
- /** Word-token spans by source for intra-word split protection. Defaults to
3461
- * spans derived from `transcripts`. */
3462
- wordsBySource?: Record<string, SilenceInterval[]>;
3463
- /** Set false to skip the desilence tighten pass entirely. Default true. */
3464
- desilence?: boolean;
3465
- /** Capability registry override (tests). */
3466
- capabilities?: Record<string, Capability>;
3467
- /** Existence of referenced asset files by ref (a stat pass gathered by the
3468
- * caller — see gatherCompileCtx). A ref mapped to false fails compile with
3469
- * "asset-missing"; refs absent from the map are not checked. */
3470
- assetsPresent?: Record<string, boolean>;
3150
+ declare function parseFileContent(data: unknown): VfsFileContent;
3151
+ interface VfsPresign {
3152
+ uploadUrl: string;
3153
+ token: string | null;
3154
+ expiresAt: string | null;
3471
3155
  }
3472
- interface CompileResult {
3473
- ok: boolean;
3474
- plan: Plan | null;
3475
- legacyEdl: Edl | null;
3476
- diagnostics: Diagnostics;
3477
- uses: string[];
3156
+ declare function parseFilePresign(data: unknown): VfsPresign;
3157
+ type RunStatus = "pending" | "running" | "completed" | "failed" | "timed_out";
3158
+ interface RunEvent {
3159
+ ts: string;
3160
+ type: string;
3161
+ payload: unknown;
3162
+ }
3163
+ interface RunState {
3164
+ id: string;
3165
+ status: RunStatus;
3166
+ errorCode: string | null;
3167
+ errorMessage: string | null;
3168
+ events: RunEvent[];
3169
+ }
3170
+ declare function parseRunState(data: unknown): RunState;
3171
+ declare function isTerminalRunStatus(s: RunStatus): boolean;
3172
+ /** One requested change in the active review round (fields are nullable —
3173
+ * a change carries only what its kind needs). */
3174
+ interface ReviewChange {
3175
+ id: string;
3176
+ idx: number;
3177
+ kind: string;
3178
+ t: number | null;
3179
+ t2: number | null;
3180
+ x: number | null;
3181
+ y: number | null;
3182
+ text: string | null;
3183
+ outcome: string | null;
3184
+ }
3185
+ interface ReviewRound {
3186
+ roundId: string;
3187
+ runId: string;
3188
+ seq: number;
3189
+ mode: string;
3190
+ status: string;
3191
+ instruction: string;
3192
+ changes: ReviewChange[];
3193
+ }
3194
+ interface ReviewSession {
3195
+ videoId: string;
3196
+ title: string | null;
3197
+ latestVersionSeq: number | null;
3198
+ activeRound: ReviewRound | null;
3199
+ /** Raw timeline.json object for the latest version, if any. */
3200
+ timeline: unknown | null;
3201
+ /** Signed download URL for the source video, if available. */
3202
+ sourceUrl: string | null;
3203
+ }
3204
+ declare class CloudHttpError extends Error {
3205
+ status: number;
3206
+ body?: unknown | undefined;
3207
+ constructor(status: number, message: string, body?: unknown | undefined);
3208
+ }
3209
+ interface ClientOptions {
3210
+ fetchImpl?: FetchLike;
3211
+ timeoutMs?: number;
3212
+ }
3213
+ declare class CloudClient {
3214
+ readonly cfg: ResolvedConfig;
3215
+ private readonly fetchImpl;
3216
+ private readonly timeoutMs;
3217
+ constructor(cfg: ResolvedConfig, opts?: ClientOptions);
3218
+ private requireVideoId;
3219
+ /** Same client (same fetch/timeout) bound to a video id — for flows that
3220
+ * create the video mid-run (init). */
3221
+ withVideoId(videoId: string): CloudClient;
3222
+ /** The injected fetch — for helpers outside the class (multipart part PUTs)
3223
+ * that need raw Response access (e.g. the ETag header). */
3224
+ get fetch(): FetchLike;
3225
+ /** POST a JSON body to an absolute API URL with auth — public wrapper over
3226
+ * the private transport for helpers living outside the class (multipart). */
3227
+ postJson(url: string, body: unknown): Promise<unknown>;
3228
+ /** POST raw binary bytes to an authenticated API URL (multipart relay
3229
+ * sub-chunks). Content-Type application/octet-stream; JSON response. */
3230
+ postBinary(url: string, bytes: Uint8Array, timeoutMs?: number): Promise<unknown>;
3231
+ private request;
3232
+ createRender(mode: RenderMode): Promise<RenderCreate>;
3233
+ getRender(renderId: string): Promise<RenderState>;
3234
+ qaFrames(args: {
3235
+ start: number;
3236
+ end: number;
3237
+ cols?: number;
3238
+ interval?: number;
3239
+ }): Promise<QaFramesResult>;
3240
+ qaWaveform(args: {
3241
+ start: number;
3242
+ end: number;
3243
+ }): Promise<QaWaveformResult>;
3244
+ qaInspect(args: {
3245
+ start: number;
3246
+ end: number;
3247
+ nFrames?: number;
3248
+ }): Promise<QaInspectResult>;
3249
+ listArtifacts(): Promise<string[]>;
3250
+ getArtifact(name: string): Promise<ArtifactContent>;
3251
+ /** POST /api/v1/assets: request an asset row + presigned upload URL
3252
+ * (clipready assets route: {kind, content_type?, video_id?, filename?} →
3253
+ * {asset_id, upload_url, upload_token, expires_at}). */
3254
+ createAsset(args: {
3255
+ kind: string;
3256
+ contentType?: string;
3257
+ filename?: string;
3258
+ }): Promise<AssetCreate>;
3259
+ /** PUT raw bytes to a presigned upload URL. No Authorization header — the
3260
+ * signed URL carries its own credentials and may be a different host.
3261
+ * Mid-stream network failures (connection resets, TLS record errors) and
3262
+ * 5xx responses are retried with exponential backoff — the whole body is
3263
+ * an in-memory buffer, so every attempt is a clean full replay. */
3264
+ upload(url: string, bytes: Uint8Array, contentType: string, timeoutMs?: number, maxAttempts?: number): Promise<void>;
3265
+ getReview(): Promise<ReviewSession>;
3266
+ /** POST a review-round event ({run_id, type, payload?}); expects {ok:true}. */
3267
+ pushReview(body: {
3268
+ run_id: string;
3269
+ type: string;
3270
+ payload?: unknown;
3271
+ }): Promise<void>;
3272
+ /** GET /v1/me — validates the credential; returns the raw payload. */
3273
+ me(): Promise<Record<string, unknown>>;
3274
+ createProject(title: string): Promise<{
3275
+ projectId: string;
3276
+ }>;
3277
+ /** POST /v1/assets for a NEW source (no video yet): {kind:"source", ...}. */
3278
+ createSourceAsset(args: {
3279
+ contentType: string;
3280
+ filename?: string;
3281
+ videoId?: string;
3282
+ }): Promise<AssetCreate>;
3283
+ /** POST /v1/projects/{id}/videos {asset_id, title?, workflow:"cli"}. */
3284
+ createVideo(projectId: string, args: {
3285
+ assetId: string;
3286
+ title?: string;
3287
+ }): Promise<{
3288
+ videoId: string;
3289
+ projectId: string;
3290
+ }>;
3291
+ filesList(): Promise<VfsFileEntry[]>;
3292
+ filesGet(path: string): Promise<VfsFileContent>;
3293
+ /** Inline PUT (≤5MB text payloads) → {path, sha256, bytes}. */
3294
+ filesPutInline(path: string, content: string, contentType?: string): Promise<{
3295
+ path: string;
3296
+ sha256: string | null;
3297
+ bytes: number | null;
3298
+ }>;
3299
+ /** Presign PUT for large/binary payloads → {upload_url, token, expires_at}. */
3300
+ filesPresign(args: {
3301
+ path: string;
3302
+ contentType: string;
3303
+ bytes: number;
3304
+ sha256: string;
3305
+ }): Promise<VfsPresign>;
3306
+ /** Commit a presigned upload → {path, status:"ready"}. */
3307
+ filesCommit(path: string): Promise<void>;
3308
+ filesDelete(path: string): Promise<void>;
3309
+ /** POST /videos/{id}/transcribe → 202 {run_id}. */
3310
+ transcribeStart(body: {
3311
+ wav_path: string;
3312
+ stem: string;
3313
+ duration_s: number;
3314
+ language?: string;
3315
+ num_speakers?: number;
3316
+ }): Promise<{
3317
+ runId: string;
3318
+ }>;
3319
+ /** POST /videos/{id}/ingest {stem?} → 202 {run_id}. Server-side probe + audio
3320
+ * extraction + transcription + screening over the uploaded source. The stem
3321
+ * names every derived artifact (transcripts/<stem>.json, audio/<stem>.wav) —
3322
+ * omit it and the server defaults to "source". */
3323
+ ingestStart(stem?: string): Promise<{
3324
+ runId: string;
3325
+ }>;
3326
+ /** POST /videos/{id}/verify {mode?, window_s?} → 202 {run_id}. The server
3327
+ * sources the preview audio itself — nothing is uploaded. */
3328
+ verifyStart(body?: {
3329
+ mode?: string;
3330
+ window_s?: number;
3331
+ }): Promise<{
3332
+ runId: string;
3333
+ }>;
3334
+ /** GET /runs/{id} — run status + events. */
3335
+ getRun(runId: string): Promise<RunState>;
3336
+ /** POST /videos/{id}/compile → {ok, plan, legacy_edl, diagnostics, uses}. */
3337
+ compileRemote(body: {
3338
+ timeline?: unknown;
3339
+ silences_by_source?: Record<string, Array<{
3340
+ start: number;
3341
+ end: number;
3342
+ }>>;
3343
+ assets_present?: Record<string, boolean>;
3344
+ }): Promise<Record<string, unknown>>;
3345
+ /** POST /videos/{id}/resolve {phrase, source?, occurrence?}. */
3346
+ resolveRemote(body: {
3347
+ phrase: string;
3348
+ source?: string;
3349
+ occurrence?: number;
3350
+ }): Promise<Record<string, unknown>>;
3351
+ /** GET /videos/{id}/compose → {html, counts, media:[{src, role}]}. */
3352
+ getCompose(): Promise<{
3353
+ html: string;
3354
+ counts: Record<string, unknown>;
3355
+ media: Array<{
3356
+ src: string;
3357
+ role: string;
3358
+ }>;
3359
+ }>;
3360
+ download(url: string, dest: string, timeoutMs?: number): Promise<{
3361
+ path: string;
3362
+ bytes: number;
3363
+ }>;
3478
3364
  }
3479
- /** Deterministic content hash of the (defaults-applied) doc: sha256 over a
3480
- * canonical (recursively key-sorted) JSON encoding. */
3481
- declare function timelineHash(doc: TimelineV2): string;
3482
- /** Compile an authored TimelineV2 into a render plan. Pure: never touches the
3483
- * filesystem; the CLI layer (io.ts) gathers ctx and writes artifacts. */
3484
- declare function compileTimeline(doc: TimelineV2, ctx?: CompileCtx): CompileResult;
3365
+ /** Download a (signed) URL to a local path, creating parent dirs. No auth
3366
+ * header signed URLs carry their own credentials and may be a different host. */
3367
+ declare function downloadTo(url: string, dest: string, opts?: {
3368
+ fetchImpl?: FetchLike;
3369
+ timeoutMs?: number;
3370
+ }): Promise<{
3371
+ path: string;
3372
+ bytes: number;
3373
+ }>;
3485
3374
 
3486
- interface TimelineFromEdlOptions {
3487
- /** Timeline id; default "tl_<uuid>". */
3488
- id?: string;
3489
- /** Known source durations (seconds) by source name. */
3490
- sourceDurations?: Record<string, number>;
3491
- /** "sha256:…" transcript hashes by source name. */
3492
- transcriptHashes?: Record<string, string>;
3375
+ declare const MAX_RUN_POLL_MS: number;
3376
+ interface RunPollOptions {
3377
+ intervalMs?: number;
3378
+ maxMs?: number;
3379
+ /** Called once per NEW event (dedup by array position). */
3380
+ onEvent?: (ev: RunEvent) => void;
3381
+ /** injectable for tests */
3382
+ sleepImpl?: (ms: number) => Promise<void>;
3383
+ now?: () => number;
3493
3384
  }
3494
- /** Lift a legacy EDL into a cut-only TimelineV2 document. */
3495
- declare function timelineFromEdl(edl: Edl, opts?: TimelineFromEdlOptions): TimelineV2;
3385
+ /** One human line per run event ("[type] message" when the payload has one). */
3386
+ declare function formatRunEvent(ev: RunEvent): string;
3387
+ /** Poll a run until terminal (completed|failed|timed_out). Returns the final
3388
+ * state; the CALLER decides what a non-completed status means. */
3389
+ declare function pollRun(client: CloudClient, runId: string, opts?: RunPollOptions): Promise<RunState>;
3390
+ /** Throw a clear error when a run ended without completing. */
3391
+ declare function assertRunCompleted(state: RunState, what: string): void;
3496
3392
 
3497
- /** Vertical 9:16 master geometry matches ensureVerticalMaster's output. */
3498
- declare const COMPOSE_WIDTH: 1080;
3499
- declare const COMPOSE_HEIGHT: 1920;
3500
- /** Entry HTML file name inside the composition dir. */
3501
- declare const COMPOSE_ENTRY: "index.html";
3502
- /** Composition-relative media paths written by the `compose` verb. */
3503
- declare const COMPOSE_VIDEO_SRC: "media/source.mp4";
3504
- declare const COMPOSE_AUDIO_SRC: "media/audio.m4a";
3505
- /** The subset of a compiled plan the composition needs. */
3506
- type ComposePlan = Pick<Plan, "segments" | "events">;
3507
- interface ComposeHtmlOptions {
3508
- /** URL (composition-relative) of the proxy video. Default media/source.mp4. */
3509
- videoSrc?: string;
3510
- /** URL (composition-relative) of the CONTINUOUS cut audio — the ffmpeg-cut
3511
- * track extracted from preview.mp4. Default media/audio.m4a. */
3512
- audioSrc?: string;
3513
- /** Declarative color grade (timeline settings.grade object form) — stamped
3514
- * as data-color-grading on the base track-0 clips. */
3515
- grade?: {
3516
- preset: string;
3517
- intensity?: number;
3518
- };
3393
+ /** Inline PUT payload ceiling (server contract: ≤5MB inline). */
3394
+ declare const INLINE_MAX_BYTES: number;
3395
+ /** sha256 hex of bytes, normalized without any "sha256:" prefix. */
3396
+ declare function sha256Hex(bytes: Uint8Array): string;
3397
+ /** Compare two sha256 values tolerating an optional "sha256:" prefix. */
3398
+ declare function sameSha(a: string | null | undefined, b: string | null | undefined): boolean;
3399
+ /** Map a VFS path to its local path under the job dir (sanitized: no "..",
3400
+ * no absolute escapes). */
3401
+ declare function vfsLocalPath(jobDir: string, path: string): string;
3402
+ /** Local job-dir path → VFS path (forward slashes, jobDir-relative). */
3403
+ declare function toVfsPath(jobDir: string, localPath: string): string;
3404
+ /** Content type for a pushed file, from its extension. */
3405
+ declare function vfsContentType(path: string): string;
3406
+ interface PulledFile {
3407
+ path: string;
3408
+ localPath: string;
3409
+ bytes: number;
3410
+ skipped: boolean;
3411
+ }
3412
+ interface FilesPullResult {
3413
+ pulled: PulledFile[];
3414
+ missing: string[];
3415
+ }
3416
+ /** Pull the listed files (or the `only` subset) into jobDir. A file whose
3417
+ * local sha256 already matches the server listing is skipped untouched. */
3418
+ declare function pullFiles(client: CloudClient, jobDir: string, opts?: {
3419
+ only?: string[];
3420
+ prefixes?: string[];
3421
+ onFile?: (f: PulledFile) => void;
3422
+ }): Promise<FilesPullResult>;
3423
+ interface PushedFile {
3424
+ path: string;
3425
+ bytes: number;
3426
+ skipped: boolean;
3427
+ mode: "inline" | "presign" | "skip";
3428
+ }
3429
+ /** Push local files (jobDir-relative paths) into the VFS. Unchanged files
3430
+ * (sha256 equal to the server listing) are skipped. Text payloads ≤5MB go
3431
+ * inline; larger/binary payloads go presign → upload → commit. */
3432
+ declare function pushFiles(client: CloudClient, jobDir: string, paths: string[], opts?: {
3433
+ onFile?: (f: PushedFile) => void;
3434
+ log?: (line: string) => void;
3435
+ }): Promise<PushedFile[]>;
3436
+ /** Upload one in-memory payload to the VFS (used for WAVs and pushed renders).
3437
+ * Payloads over 8MB go chunked (multipart, per-part retry) when the server
3438
+ * supports it; otherwise — and for small payloads — the classic presign →
3439
+ * single PUT → commit flow. */
3440
+ declare function uploadVfsPayload(client: CloudClient, vfsPath: string, bytes: Uint8Array, contentType: string, opts?: {
3441
+ sha256?: string;
3442
+ log?: (line: string) => void;
3443
+ }): Promise<void>;
3444
+ /** Back-compat alias: upload one in-memory payload to the VFS. */
3445
+ declare function uploadVfsBytes(client: CloudClient, vfsPath: string, bytes: Uint8Array, contentType: string, log?: (line: string) => void): Promise<void>;
3446
+
3447
+ /** Payloads LARGER than this go multipart; at or below, the single-shot path. */
3448
+ declare const MULTIPART_THRESHOLD_BYTES: number;
3449
+ /** Direct part-PUT transport failures before the relay fallback kicks in. */
3450
+ declare const RELAY_DIRECT_ATTEMPTS = 3;
3451
+ /** Relay sub-chunk ceiling (3.5MB — under the API route's body limit). */
3452
+ declare const RELAY_SUBCHUNK_BYTES = 3670016;
3453
+ /** Multipart applies above the 8MB threshold. Accepts a byte count (streaming
3454
+ * callers stat the file) or an in-memory payload. */
3455
+ declare function shouldMultipart(sizeOrBytes: number | Uint8Array): boolean;
3456
+ /** A random-access byte source: only `len`-sized windows are ever materialized,
3457
+ * so uploads stay 2GB-safe (memory ≤ concurrency × part_size). */
3458
+ interface MultipartSource {
3459
+ size: number;
3460
+ /** Read exactly min(len, size - offset) bytes starting at offset. */
3461
+ readSlice(offset: number, len: number): Promise<Buffer>;
3462
+ }
3463
+ /** Wrap an in-memory payload as a source (small VFS pushes). */
3464
+ declare function bytesSource(bytes: Uint8Array): MultipartSource;
3465
+ interface FileSource extends MultipartSource {
3466
+ /** Close the underlying fd. Callers MUST close on completion AND failure. */
3467
+ close(): Promise<void>;
3468
+ }
3469
+ /** File-backed source: fs/promises open + positional reads. Never loads the
3470
+ * whole file; concurrent readSlice calls are safe (positional pread). */
3471
+ declare function openFileSource(path: string): Promise<FileSource>;
3472
+ interface MultipartOptions {
3473
+ kind: "asset" | "jobfile";
3474
+ /** In-memory payload (small VFS pushes). Exactly one of bytes/source. */
3475
+ bytes?: Uint8Array;
3476
+ /** Streaming payload (init sources) — 2GB-safe. */
3477
+ source?: MultipartSource;
3478
+ contentType: string;
3479
+ /** kind "asset": original filename (no video exists yet at init time). */
3480
+ filename?: string;
3481
+ /** kind "jobfile": owning video + VFS path (+ sha256 for integrity). */
3482
+ videoId?: string;
3483
+ path?: string;
3484
+ sha256?: string;
3485
+ log?: (line: string) => void;
3486
+ /** Retry backoff schedule override (tests) — defaults to jitteredBackoffMs. */
3487
+ backoffMs?: (attempt: number) => number;
3519
3488
  }
3520
- /** Element counts as emitted by composeHtml (mirrors its skip rules). */
3521
- interface ComposeCounts {
3522
- segments: number;
3523
- cues: number;
3524
- zooms: number;
3525
- overlays: number;
3489
+ /** Chunked upload of a payload (in-memory bytes OR a streaming source).
3490
+ * Returns "unavailable" ONLY when the server cannot do multipart at all
3491
+ * (503 multipart_unavailable / 404 / 405 on session create) so the caller can
3492
+ * fall back to the single-shot presign path. Any other failure aborts the
3493
+ * session (best-effort) and throws. Parts are sliced through
3494
+ * source.readSlice, so memory stays ≤ PART_CONCURRENCY × part_size. */
3495
+ declare function uploadMultipart(client: CloudClient, opts: MultipartOptions, timeoutMs?: number): Promise<{
3496
+ assetId?: string;
3497
+ } | "unavailable">;
3498
+
3499
+ interface JobFile {
3500
+ video_id: string;
3501
+ project_id: string;
3502
+ api_base: string;
3503
+ stem: string;
3504
+ duration_s: number;
3505
+ /** Absolute path of the original init source (used as the transcribe media
3506
+ * fallback and to re-bake vertical_src.mp4 on demand). Optional: job.json
3507
+ * files written before 2.0.1 don't have it. */
3508
+ source_path?: string;
3526
3509
  }
3527
- declare function composeCounts(plan: ComposePlan): ComposeCounts;
3528
- /** Declarative HyperFrames composition markup see module doc. Pure. */
3529
- declare function composeHtml(plan: ComposePlan, options?: ComposeHtmlOptions): string;
3510
+ declare function jobJsonPath(jobDir: string): string;
3511
+ declare function writeJobFile(jobDir: string, job: JobFile): string;
3512
+ declare function readJobFile(jobDir: string): JobFile | null;
3513
+ /** Video id fallback for job-dir verbs: flag/env (already resolved) → job.json. */
3514
+ declare function videoIdFromJob(jobDir: string, resolved: string | undefined): string;
3530
3515
 
3531
- declare function timelineJsonPath(jobDir: string): string;
3532
- declare function planPath(jobDir: string): string;
3533
- declare function diagnosticsPath(jobDir: string): string;
3534
- interface LoadedTimeline {
3516
+ /** Sanitize a source-derived stem to the server's filename-safe charset
3517
+ * (letters, digits, `.` `_` `-`): every run of anything else becomes a single
3518
+ * `-`, leading/trailing `-` are trimmed. Apostrophes, spaces, unicode
3519
+ * punctuation etc. in the source filename must never leak into the stem —
3520
+ * the server rejects them ("stem must be a filename-safe string"). Falls back
3521
+ * to "source" when nothing survives. */
3522
+ declare function sanitizeStem(name: string): string;
3523
+ /** The artifact set init pulls after the ingest run completes (plus the
3524
+ * prompts/ prefix). probe.json carries the server-side media probe. */
3525
+ declare function ingestArtifactSet(stem: string): string[];
3526
+ /** Read duration_s out of a pulled probe.json ({duration_s, width, height,
3527
+ * rotation}). Throws when the file is absent or carries no usable duration. */
3528
+ declare function readProbeDuration(jobDir: string): number;
3529
+ interface InitOptions {
3530
+ source: string;
3531
+ jobDir?: string;
3532
+ title?: string;
3533
+ pollIntervalMs?: number;
3534
+ verbose?: boolean;
3535
+ }
3536
+ interface InitResult {
3537
+ jobDir: string;
3538
+ jobJson: string;
3539
+ videoId: string;
3540
+ projectId: string;
3541
+ stem: string;
3542
+ durationS: number;
3543
+ uploaded: string;
3544
+ uploadedBytes: number;
3545
+ runId: string;
3546
+ pulled: FilesPullResult;
3547
+ /** Watch/review page in the clipready web app. */
3548
+ webUrl: string;
3549
+ }
3550
+ declare function initCloudJob(client: CloudClient, opts: InitOptions): Promise<InitResult>;
3551
+
3552
+ interface Transcribe2Options {
3553
+ stem: string;
3554
+ pollIntervalMs?: number;
3555
+ verbose?: boolean;
3556
+ }
3557
+ interface Transcribe2Result {
3558
+ /** null when the artifacts already existed server-side (pull only). */
3559
+ runId: string | null;
3560
+ pulled: FilesPullResult;
3561
+ }
3562
+ /** The artifact set `transcribe` pulls (probe.json rides along since v3 —
3563
+ * it feeds job.json duration_s after an ingest re-run). */
3564
+ declare function transcribeArtifactSet(stem: string): string[];
3565
+ declare function transcribeCloud(client: CloudClient, jobDir: string, opts: Transcribe2Options): Promise<Transcribe2Result>;
3566
+
3567
+ declare function timelineJsonPath2(jobDir: string): string;
3568
+ declare function planPath2(jobDir: string): string;
3569
+ declare function diagnosticsPath2(jobDir: string): string;
3570
+ interface LoadedTimeline2 {
3535
3571
  doc: TimelineV2 | null;
3536
- errors: Diagnostic[];
3537
3572
  raw: unknown;
3573
+ errors: Diagnostic[];
3538
3574
  }
3539
- /** Read + parse <job>/timeline.json. */
3540
- declare function loadTimeline(jobDir: string): LoadedTimeline;
3541
- /** Transcript file for a source name. Mirrors silence.ts loadWordsBySource:
3542
- * transcripts/<name>.json, else the lone transcripts/*.json (single-source
3543
- * jobs name the transcript by source stem). */
3544
- declare function transcriptFileForSource(jobDir: string, name: string): string | null;
3545
- declare function sha256File(path: string): string;
3546
- interface GatherOptions {
3547
- /** Skip ffmpeg silence detection (ctx.silencesBySource stays undefined and
3548
- * the compile desilence pass becomes a no-op tighten). */
3549
- noDetect?: boolean;
3575
+ /** Read + parse <job>/timeline.json (public mirror of io.ts loadTimeline). */
3576
+ declare function loadTimeline2(jobDir: string): LoadedTimeline2;
3577
+ /** Local (client-side) validation verdict: schema + semantic + capabilities. */
3578
+ declare function validateLocal(doc: TimelineV2): {
3579
+ ok: boolean;
3580
+ uses: string[];
3581
+ errors: Diagnostic[];
3582
+ warnings: Diagnostic[];
3583
+ };
3584
+ /** Stat pass over asset refs so the server compile can emit asset-missing. */
3585
+ declare function statAssetsForDoc(jobDir: string, doc: TimelineV2): Record<string, boolean>;
3586
+ interface CompileRemoteResult {
3587
+ ok: boolean;
3588
+ planPath?: string;
3589
+ edlPath?: string;
3590
+ diagnosticsPath: string;
3591
+ response: Record<string, unknown>;
3550
3592
  }
3551
- /** Gather the impure CompileCtx inputs for a doc: per-source transcripts +
3552
- * hashes, and (unless noDetect) ffmpeg silencedetect maps using the doc's
3553
- * desilence settings the same detection the legacy desilence flow runs. */
3554
- declare function gatherCompileCtx(jobDir: string, doc: TimelineV2, opts?: GatherOptions): Promise<CompileCtx>;
3555
- interface WrittenArtifacts {
3556
- plan?: string;
3557
- diagnostics: string;
3558
- edl?: string;
3593
+ /** POST the raw timeline (+ local asset stats) to the server compile and
3594
+ * write plan/diagnostics/edl exactly as returned (verbatim JSON). The server
3595
+ * sources silences from the VFS no silences_by_source from the client. */
3596
+ declare function compileRemote(client: CloudClient, jobDir: string, loaded: LoadedTimeline2): Promise<CompileRemoteResult>;
3597
+
3598
+ interface Verify2Options {
3599
+ full?: boolean;
3600
+ windowS?: number;
3601
+ pollIntervalMs?: number;
3602
+ verbose?: boolean;
3559
3603
  }
3560
- /** Write resolved/plan.json + resolved/diagnostics.json (+ legacy edl.json on
3561
- * success). diagnostics.json is always written, also for failed compiles. */
3562
- declare function writeCompileArtifacts(jobDir: string, result: CompileResult): WrittenArtifacts;
3563
- /** Update ONLY the captions block of timeline.json (enabled + preset), leaving
3564
- * every other byte-level detail of the authored doc as-is. */
3565
- declare function updateCaptionsBlock(jobDir: string, preset: string): {
3566
- raw: Record<string, unknown>;
3567
- };
3604
+ interface Verify2Result {
3605
+ runId: string;
3606
+ verifyPath: string;
3607
+ verdict: string | null;
3608
+ findings: number | null;
3609
+ }
3610
+ declare function verifyCloud(client: CloudClient, jobDir: string, opts?: Verify2Options): Promise<Verify2Result>;
3611
+
3612
+ /** QA output dir: <job>/qa when a job dir is given, else ./qa in cwd. */
3613
+ declare function qaDir(jobDir?: string): string;
3614
+ declare function framesPngPath(dir: string, start: number, end: number, suffix?: string): string;
3615
+ declare function waveformPngPath(dir: string, start: number, end: number): string;
3616
+ declare function waveformWordsPath(dir: string, start: number, end: number): string;
3617
+ /** The slice of a plan segment the QA math needs. */
3618
+ interface QaPlanSegment {
3619
+ srcStart: number;
3620
+ srcEnd: number;
3621
+ outStart: number;
3622
+ outEnd: number;
3623
+ }
3624
+ /** Read the segments array out of <job>/resolved/plan.json. */
3625
+ declare function readPlanSegments(jobDir: string): QaPlanSegment[];
3626
+ /** A source-time QA window; `suffix` distinguishes multi-window requests
3627
+ * ("-a"/"-b"/… — empty for a single window). */
3628
+ interface QaSourceWindow {
3629
+ start: number;
3630
+ end: number;
3631
+ suffix: string;
3632
+ }
3633
+ /** Source-time windows around join `i` (1-based: the boundary between plan
3634
+ * segments i and i+1): the last `spanS` seconds of segment i (tail, "-a")
3635
+ * and the first `spanS` seconds of segment i+1 (head, "-b"), each clamped
3636
+ * to its segment. */
3637
+ declare function joinSourceWindows(segments: QaPlanSegment[], joinIndex: number, spanS: number): QaSourceWindow[];
3638
+ /** Map an OUTPUT-time window onto source-time windows: intersect
3639
+ * [outStart, outEnd] with each segment's output range and shift into source
3640
+ * time (srcT = seg.srcStart + (outT - seg.outStart)). One window per
3641
+ * overlapped segment, suffixed -a/-b/… when the window crosses a join. */
3642
+ declare function outWindowToSourceWindows(segments: QaPlanSegment[], outStart: number, outEnd: number): QaSourceWindow[];
3643
+
3644
+ declare function reviewDir2(jobDir: string): string;
3645
+ declare function roundJsonPath2(jobDir: string): string;
3646
+ declare function instructionPath2(jobDir: string): string;
3647
+ interface PullReview2Result {
3648
+ jobDir: string;
3649
+ round: ReviewRound | null;
3650
+ wroteTimeline: boolean;
3651
+ downloadedSource: string | null;
3652
+ }
3653
+ /** GET the review session and hydrate the job dir (round.json + instruction.md,
3654
+ * timeline.json only when absent, optional source download). */
3655
+ declare function pullReview2(client: CloudClient, jobDir: string, opts?: {
3656
+ withSource?: boolean;
3657
+ }): Promise<PullReview2Result>;
3658
+ /** run id = flag → <job>/review/round.json run_id. */
3659
+ declare function resolveRunId2(jobDir: string, flagOverride?: string): string;
3660
+ declare const REVIEW_STAGES2: readonly ["provisioning", "transcribing", "edl", "render_preview", "qc", "render_final", "uploading", "log"];
3661
+ declare function pushStage2(client: CloudClient, runId: string, stage: string, message?: string): Promise<void>;
3662
+ declare function assetContentType2(ref: string): string;
3663
+ /** Ordered, de-duplicated asset refs from a server-compiled plan's events. */
3664
+ declare function collectPlanAssetRefs2(plan: unknown): string[];
3665
+ /** Upload the plan's referenced assets (sha-cached in review/assets.json);
3666
+ * returns the {ref: asset_id} manifest. */
3667
+ declare function syncAssets2(client: CloudClient, jobDir: string, plan: unknown): Promise<Record<string, string>>;
3668
+ interface Push2Failure {
3669
+ ok: false;
3670
+ errors: Array<{
3671
+ path: string;
3672
+ code: string;
3673
+ message: string;
3674
+ }>;
3675
+ diagnostics: unknown | null;
3676
+ }
3677
+ interface PushTimeline2Success {
3678
+ ok: true;
3679
+ plan: unknown;
3680
+ timelineHash: string | null;
3681
+ assets: Record<string, string>;
3682
+ }
3683
+ declare function pushTimeline2(client: CloudClient, jobDir: string, runId: string): Promise<Push2Failure | PushTimeline2Success>;
3684
+ interface ChangeOutcome2 {
3685
+ change_id: string;
3686
+ ok: boolean;
3687
+ note?: string;
3688
+ }
3689
+ /** Read an outcomes file (bare array or {outcomes:[...]}) — mirror of
3690
+ * cloud/review.ts readOutcomesFile. */
3691
+ declare function readOutcomesFile2(path: string): ChangeOutcome2[];
3692
+ interface PushComplete2Success {
3693
+ ok: true;
3694
+ plan: unknown;
3695
+ durationS: number;
3696
+ outcomes: ChangeOutcome2[] | undefined;
3697
+ assets: Record<string, string>;
3698
+ }
3699
+ declare function pushComplete2(client: CloudClient, jobDir: string, runId: string, opts: {
3700
+ durationS: number;
3701
+ outcomesPath?: string;
3702
+ }): Promise<Push2Failure | PushComplete2Success>;
3703
+
3704
+ declare const MAX_POLL_MS: number;
3705
+ declare function defaultRenderOut(jobDir: string, mode: RenderMode): string;
3706
+ interface PollOptions {
3707
+ intervalMs?: number;
3708
+ maxMs?: number;
3709
+ onTick?: (state: RenderState, elapsedMs: number) => void;
3710
+ /** injectable for tests */
3711
+ sleepImpl?: (ms: number) => Promise<void>;
3712
+ now?: () => number;
3713
+ }
3714
+ /** Poll GET /renders/{id} until the status is terminal (completed|failed) or the
3715
+ * deadline is hit. Returns the last observed state. Throws on timeout. */
3716
+ declare function pollRender(client: CloudClient, renderId: string, opts?: PollOptions): Promise<RenderState>;
3717
+
3718
+ /** Locate the bundled skill dir. Works both in dev (tsx: src/cloud/skill.ts →
3719
+ * <repo>/skills) and from the built/installed package (dist/cli.js →
3720
+ * <pkg>/skills). Searches a few relative candidates and picks the first that
3721
+ * actually contains a SKILL.md. */
3722
+ declare function bundledSkillDir(): string;
3723
+ declare function defaultSkillTarget(): string;
3724
+ interface InstallResult {
3725
+ source: string;
3726
+ dest: string;
3727
+ files: string[];
3728
+ }
3729
+ /** Copy the bundled skill into <target|~/.claude/skills>/video-editing,
3730
+ * overwriting any existing copy. Returns the installed file list (relative). */
3731
+ declare function installSkill(opts?: {
3732
+ target?: string;
3733
+ }): InstallResult;
3734
+
3735
+ declare function publicConfigDir(): string;
3736
+ declare function publicConfigEnvPath(): string;
3737
+ declare const MANAGED_KEYS: readonly ["CLIPREADY_API_KEY", "CLIPREADY_API_BASE"];
3738
+ type ManagedKey = (typeof MANAGED_KEYS)[number];
3739
+ /** Load CLIPREADY_* values from the config file into process.env for any that
3740
+ * are not already set (env always wins). Call once at CLI startup so
3741
+ * `config set-key` actually takes effect for every verb. */
3742
+ declare function loadPublicConfigEnv(env?: NodeJS.ProcessEnv): void;
3743
+ /** Write/update ONE managed key in the config file, preserving other lines. */
3744
+ declare function savePublicConfigKey(key: ManagedKey, value: string): string;
3568
3745
 
3569
- export { type Anchor, type AudioItem, CAPABILITIES, COMPOSE_AUDIO_SRC, COMPOSE_ENTRY, COMPOSE_HEIGHT, COMPOSE_VIDEO_SRC, COMPOSE_WIDTH, type Capability, type CapabilityStatus, type CaptionOverride, CommandError, type CompileCtx, type CompileResult, type ComposeCounts, type ComposeHtmlOptions, type ComposePlan, type CutBoundary, type CutRange, DEFAULT_DESILENCE, DEFAULT_EDIT_ROOT, type DesilenceOptions, type DesilenceStats, type Diagnostic, type Diagnostics, type Edl, type EdlOverlay, type EdlRange, type FeatureKey, type FfprobeResult, type FfprobeStream, type OutputAnchor, type OverlayItem, PRESETS, type PhraseMatch, type PhraseSuggestion, type Plan, type PlanAudioEvent, type PlanCaptionCueEvent, type PlanCueWord, type PlanEvent, type PlanOverlayEvent, type PlanSegment, type PlanVideoOp, type PlanZoomOp, type Provenance, type RenderMode, type ResolveOutcome, type ReviewIssue, type ReviewJson, type ScribeTranscript, type ScribeWord, type SilenceInterval, type SourceAnchor, type SourceJson, type StreamWord, type TimelineFromEdlOptions, type TimelineSettings, type TimelineSource, type TimelineV2, type TokenType, VIDEO_EXTS, type VerifyJoinFinding, type VerifyResult, type Warning, type WordsAnchor, type ZoomItem, agentizePrompt, artifactPaths, autoGradeForClip, checkCapabilities, compileTimeline, composeCounts, composeHtml, computeEnvelope, configDir, configEnvPath, cutBoundaries, decideGradeFilter, defaultJobDir, desilenceEdl, detectSilences, diagnosticsPath, displayedDimensions, duplicatedPhrase, duplicatedPhrases, duplicatedSpans, edlPath, ensureTranscribed, ensureVerticalMaster, ffprobe, findPhrase, fuzzySuggestions, gatherCompileCtx, generateQcImages, getPreset, initJob, isoNow, learnedEditingGuardrails, loadApiKey, loadJob, loadTimeline, loadWords, loadWordsBySource, mediaDuration, normalizePhrase, normalizeToken, packTranscripts, parseSilenceDetect, parseTimeline, planPath, pyFixed, pyFixedPad, pyRound, pySpacePad, readEdl, render, requireFfmpeg, resolveJob, resolvePhrase, rotationDegrees, run, runSync, screenTakes, sha256File, shown, silenceDetectArgs, slugify, sourceFromJob, statusPayload, straddlingGap, tightenRange, tightenRanges, timelineFromEdl, timelineHash, timelineJsonPath, timelineV2Schema, timelineView, transcribeBatch, transcribeOne, transcriptFileForSource, transcriptPath, updateCaptionsBlock, usesFromDoc, validateTimeline, verify, videoStream, wordStream, writeAgentManifest, writeBoundaries, writeCompileArtifacts, writeCoverage, writeSilencePrompt, writeTranscriptArtifacts, writeVideoEditingPrompt, writeWarningsFromReview, writeWordDump };
3746
+ export { type Anchor, type AssetCreate, type AudioItem, CAPABILITIES, CAPTION_STYLE_DEFAULTS, type Capability, type CapabilityStatus, type CaptionOverride, type CaptionPreset, type CaptionStyle, type ChangeOutcome2, type ClientOptions, CloudClient, type CloudFlags, CloudHttpError, type CompileRemoteResult, type CutBoundary, type CutRange, DEFAULT_DOWNLOAD_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, type Diagnostic, type Diagnostics, type Edl, type EdlRange, type FeatureKey, type FetchLike, type FileSource, type FilesPullResult, type GradeSetting, INLINE_MAX_BYTES, type InitOptions, type InitResult, type InstallResult, type JobFile, type LoadedTimeline2, MAX_POLL_MS, MAX_RUN_POLL_MS, MULTIPART_THRESHOLD_BYTES, type MultipartOptions, type MultipartSource, type OutputAnchor, type OverlayFx, type OverlayItem, type ParseResult, type PhraseMatch, type PhraseSuggestion, type PollOptions, type Provenance, type PullReview2Result, type PulledFile, type Push2Failure, type PushComplete2Success, type PushTimeline2Success, type PushedFile, type QaPlanSegment, type QaSourceWindow, RELAY_DIRECT_ATTEMPTS, RELAY_SUBCHUNK_BYTES, REVIEW_STAGES2, type RenderCreate, type RenderMode, type RenderState, type RenderStatus, type ResolveOptions, type ResolveOutcome, type ResolvedConfig, type ReviewChange, type ReviewRound, type ReviewSession, type RunEvent, type RunPollOptions, type RunState, type RunStatus, type ScribeTranscript, type ScribeWord, type SourceAnchor, type StreamWord, type TimelineSettings, type TimelineSource, type TimelineV2, type Transcribe2Options, type Transcribe2Result, type Verify2Options, type Verify2Result, type VerifyJoinFinding, type VerifyResult, type VfsFileContent, type VfsFileEntry, type VfsPresign, type WordsAnchor, type ZoomItem, assertRunCompleted, assetContentType2, assetsUrl, bundledSkillDir, bytesSource, checkCapabilities, collectPlanAssetRefs2, compileRemote, compileUrl, composeUrl, defaultRenderOut, defaultSkillTarget, diagnosticsPath2, downloadTo, emptyDiagnostics, filesUrl, findPhrase, formatRunEvent, framesPngPath, fuzzySuggestions, ingestArtifactSet, ingestUrl, initCloudJob, installSkill, instructionPath2, isTerminalRunStatus, isTerminalStatus, jobJsonPath, joinSourceWindows, loadPublicConfigEnv, loadTimeline2, meUrl, multipartRelayUrl, multipartUrl, normalizePhrase, normalizeToken, openFileSource, outWindowToSourceWindows, parseFileContent, parseFileList, parseFilePresign, parseRenderCreate, parseRenderState, parseRunState, parseTimeline, planPath2, pollRender, pollRun, projectVideosUrl, projectsUrl, publicConfigDir, publicConfigEnvPath, pullFiles, pullReview2, pushComplete2, pushFiles, pushStage2, pushTimeline2, qaDir, readJobFile, readOutcomesFile2, readPlanSegments, readProbeDuration, renderUrl, rendersUrl, resolveApiBase, resolveApiKey, resolveConfig, resolvePhrase, resolveRunId2, resolveUrl, resolveVideoId, reviewDir2, reviewUrl, roundJsonPath2, runUrl, sameSha, sanitizeStem, savePublicConfigKey, sha256Hex, shouldMultipart, statAssetsForDoc, stripTrailingSlash, syncAssets2, timelineJsonPath2, timelineV2Schema, toVfsPath, transcribeArtifactSet, transcribeCloud, transcribeUrl, uploadMultipart, uploadVfsBytes, uploadVfsPayload, usesFromDoc, validateLocal, validateTimeline, verifyCloud, verifyUrl, vfsContentType, vfsLocalPath, videoIdFromJob, waveformPngPath, waveformWordsPath, wordStream, writeJobFile };