@plaintake/scenario 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ export declare const EXIT_CODES: {
2
+ readonly ok: 0;
3
+ readonly assertion: 1;
4
+ readonly usage: 2;
5
+ readonly toolchain: 3;
6
+ readonly capture: 4;
7
+ readonly render: 5;
8
+ readonly verification: 6;
9
+ };
10
+ export type FailureKind = Exclude<keyof typeof EXIT_CODES, 'ok'>;
11
+ /**
12
+ * Satisfies the structural exit-code contract: any thrown error carrying a numeric
13
+ * `exitCode` maps to that process exit code.
14
+ */
15
+ export declare class DemoError extends Error {
16
+ readonly kind: FailureKind;
17
+ readonly exitCode: number;
18
+ readonly details: Record<string, unknown>;
19
+ constructor(kind: FailureKind, message: string, details?: Record<string, unknown>);
20
+ }
@@ -0,0 +1,58 @@
1
+ import { z } from 'zod';
2
+ export declare const DemoEventTypeSchema: z.ZodEnum<{
3
+ "session.start": "session.start";
4
+ "session.finish": "session.finish";
5
+ chapter: "chapter";
6
+ "step.start": "step.start";
7
+ "step.finish": "step.finish";
8
+ "assertion.pass": "assertion.pass";
9
+ "assertion.fail": "assertion.fail";
10
+ "privacy.mask": "privacy.mask";
11
+ "human.wait": "human.wait";
12
+ }>;
13
+ export declare const TargetSchema: z.ZodObject<{
14
+ role: z.ZodOptional<z.ZodString>;
15
+ accessibleName: z.ZodOptional<z.ZodString>;
16
+ selector: z.ZodOptional<z.ZodString>;
17
+ rect: z.ZodOptional<z.ZodObject<{
18
+ x: z.ZodNumber;
19
+ y: z.ZodNumber;
20
+ width: z.ZodNumber;
21
+ height: z.ZodNumber;
22
+ }, z.core.$strip>>;
23
+ }, z.core.$strip>;
24
+ export declare const DemoEventSchema: z.ZodObject<{
25
+ schema: z.ZodLiteral<"agent-demo.event/v1">;
26
+ seq: z.ZodNumber;
27
+ tStartNs: z.ZodString;
28
+ tEndNs: z.ZodOptional<z.ZodString>;
29
+ type: z.ZodEnum<{
30
+ "session.start": "session.start";
31
+ "session.finish": "session.finish";
32
+ chapter: "chapter";
33
+ "step.start": "step.start";
34
+ "step.finish": "step.finish";
35
+ "assertion.pass": "assertion.pass";
36
+ "assertion.fail": "assertion.fail";
37
+ "privacy.mask": "privacy.mask";
38
+ "human.wait": "human.wait";
39
+ }>;
40
+ stableId: z.ZodOptional<z.ZodString>;
41
+ title: z.ZodOptional<z.ZodString>;
42
+ subtitle: z.ZodOptional<z.ZodString>;
43
+ target: z.ZodOptional<z.ZodObject<{
44
+ role: z.ZodOptional<z.ZodString>;
45
+ accessibleName: z.ZodOptional<z.ZodString>;
46
+ selector: z.ZodOptional<z.ZodString>;
47
+ rect: z.ZodOptional<z.ZodObject<{
48
+ x: z.ZodNumber;
49
+ y: z.ZodNumber;
50
+ width: z.ZodNumber;
51
+ height: z.ZodNumber;
52
+ }, z.core.$strip>>;
53
+ }, z.core.$strip>>;
54
+ payload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
55
+ }, z.core.$strip>;
56
+ export type DemoEvent = z.infer<typeof DemoEventSchema>;
57
+ export type DemoEventType = z.infer<typeof DemoEventTypeSchema>;
58
+ export type Target = z.infer<typeof TargetSchema>;
@@ -0,0 +1,9 @@
1
+ export * from './errors.js';
2
+ export * from './event.js';
3
+ export * from './json-schema.js';
4
+ export * from './manifest.js';
5
+ export * from './render-plan.js';
6
+ export * from './results.js';
7
+ export * from './scenario.js';
8
+ export * from './stable-json.js';
9
+ export * from './zod-format.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The published counterpart to `ScenarioMetaSchema`, for an agent or IDE that wants to
3
+ * validate scenario metadata without this package's source or a running `demo_validate`.
4
+ *
5
+ * `io: 'input'` on purpose: a scenario author never supplies `handoff`, `handoffTimeoutMs` or
6
+ * `allowedConsoleErrors`, so the schema a caller validates *against* must mark them optional,
7
+ * even though `ScenarioMeta` (the parsed *output* type) always carries them.
8
+ */
9
+ export declare function scenarioJsonSchema(): Record<string, unknown>;
@@ -0,0 +1,46 @@
1
+ import { z } from 'zod';
2
+ export declare const BundleManifestSchema: z.ZodObject<{
3
+ schema: z.ZodLiteral<"agent-demo.bundle/v1">;
4
+ status: z.ZodEnum<{
5
+ passed: "passed";
6
+ failed: "failed";
7
+ }>;
8
+ scenario: z.ZodObject<{
9
+ id: z.ZodString;
10
+ sourceSha256: z.ZodString;
11
+ }, z.core.$strip>;
12
+ toolchain: z.ZodObject<{
13
+ node: z.ZodString;
14
+ playwright: z.ZodString;
15
+ chromiumRevision: z.ZodString;
16
+ ffmpeg: z.ZodString;
17
+ libass: z.ZodString;
18
+ fontSha256: z.ZodString;
19
+ containerImage: z.ZodOptional<z.ZodString>;
20
+ }, z.core.$strip>;
21
+ environment: z.ZodObject<{
22
+ os: z.ZodString;
23
+ architecture: z.ZodString;
24
+ locale: z.ZodLiteral<"en-US">;
25
+ timezone: z.ZodLiteral<"UTC">;
26
+ viewport: z.ZodTuple<[z.ZodLiteral<1920>, z.ZodLiteral<1080>], null>;
27
+ deviceScaleFactor: z.ZodLiteral<1>;
28
+ }, z.core.$strip>;
29
+ assertions: z.ZodArray<z.ZodObject<{
30
+ id: z.ZodString;
31
+ status: z.ZodEnum<{
32
+ passed: "passed";
33
+ failed: "failed";
34
+ }>;
35
+ }, z.core.$strip>>;
36
+ artifacts: z.ZodArray<z.ZodObject<{
37
+ path: z.ZodString;
38
+ sha256: z.ZodString;
39
+ bytes: z.ZodNumber;
40
+ role: z.ZodEnum<{
41
+ source: "source";
42
+ derived: "derived";
43
+ }>;
44
+ }, z.core.$strip>>;
45
+ }, z.core.$strip>;
46
+ export type BundleManifest = z.infer<typeof BundleManifestSchema>;
@@ -0,0 +1,454 @@
1
+ import { z } from 'zod';
2
+ export declare const CueSchema: z.ZodObject<{
3
+ id: z.ZodString;
4
+ startMs: z.ZodNumber;
5
+ endMs: z.ZodNumber;
6
+ lines: z.ZodArray<z.ZodString>;
7
+ }, z.core.$strip>;
8
+ /**
9
+ * The closing credit card. Absent means no card, which is a Pro-Tier-only outcome.
10
+ *
11
+ * This lives in the **plan**, not in a licence check inside the renderer, and that is the
12
+ * single most important thing about it. The plan is built once, when the bundle is
13
+ * created, from the licence state at that moment; rendering then executes it verbatim. So
14
+ * a bundle produces the same bytes on a licensed and an unlicensed machine, and
15
+ * `compareRenders`, `refreshManifestArtifacts` and every reproducibility claim keep
16
+ * holding. Reading licence state at render time would have voided all of them.
17
+ *
18
+ * The colour regexes are also the injection guard: these values reach an FFmpeg
19
+ * filtergraph, and on the Pro Tier they come from user configuration. `#RRGGBB` admits no
20
+ * filtergraph metacharacter. The *text* never reaches the filtergraph at all — it goes
21
+ * into `captions/outro.ass`.
22
+ */
23
+ export declare const OutroSchema: z.ZodObject<{
24
+ durationMs: z.ZodNumber;
25
+ lines: z.ZodArray<z.ZodString>;
26
+ backgroundColor: z.ZodString;
27
+ textColor: z.ZodString;
28
+ assPath: z.ZodLiteral<"captions/outro.ass">;
29
+ }, z.core.$strip>;
30
+ export type Outro = z.infer<typeof OutroSchema>;
31
+ /**
32
+ * The opening card. Absent means the video starts on the recording, which is what every
33
+ * plan before this did and what any scenario declaring no `intro` still does.
34
+ *
35
+ * Structurally the outro's twin, and frozen for the same reason: the plan is built once,
36
+ * when the bundle is created, and rendering executes it verbatim. The colour regexes carry
37
+ * the same duty here — these values reach an FFmpeg filtergraph and `#RRGGBB` admits no
38
+ * metacharacter, while the *text* never goes near it and lands in `captions/intro.ass`.
39
+ *
40
+ * What differs is upstream, not here: the outro's content is decided by the licence, the
41
+ * intro's by the scenario. By the time either reaches the plan that distinction is spent —
42
+ * both are just a card to draw.
43
+ *
44
+ * The spoken hook is **not** in this schema. It is synthesised at record time into
45
+ * `speech/clips/intro.wav` like any step's narration, and reaches the render through
46
+ * `speech` with every other clip. A plan that carried the text would be a plan whose
47
+ * rendering depended on a voice model being installed.
48
+ */
49
+ export declare const IntroSchema: z.ZodObject<{
50
+ durationMs: z.ZodNumber;
51
+ lines: z.ZodArray<z.ZodString>;
52
+ backgroundColor: z.ZodString;
53
+ textColor: z.ZodString;
54
+ assPath: z.ZodLiteral<"captions/intro.ass">;
55
+ }, z.core.$strip>;
56
+ export type Intro = z.infer<typeof IntroSchema>;
57
+ /**
58
+ * One MP4 chapter marker. `endMs` is stored rather than derived from the next mark, so
59
+ * `render/chapters.ffmetadata` is a pure function of the plan and nothing is recomputed at
60
+ * render time — the same reason `ffmpeg` holds literal argument arrays.
61
+ */
62
+ export declare const ChapterMarkSchema: z.ZodObject<{
63
+ startMs: z.ZodNumber;
64
+ endMs: z.ZodNumber;
65
+ title: z.ZodString;
66
+ }, z.core.$strip>;
67
+ /**
68
+ * Chapter markers. Only ever produced on the Pro Tier — but nothing here knows that,
69
+ * and that is the point: the tier decision is taken once when the bundle is created, and the
70
+ * renderer executes what it finds.
71
+ *
72
+ * Unlike the outro's colours, chapter titles are arbitrary scenario text and cannot be
73
+ * restricted to a safe character set. They never reach a filtergraph — they go into
74
+ * `render/chapters.ffmetadata`, which is passed as an `-i` input and where `=`, `;`, `#`,
75
+ * `\` and newline are backslash-escaped.
76
+ */
77
+ export declare const ChaptersSchema: z.ZodObject<{
78
+ metadataPath: z.ZodLiteral<"render/chapters.ffmetadata">;
79
+ marks: z.ZodArray<z.ZodObject<{
80
+ startMs: z.ZodNumber;
81
+ endMs: z.ZodNumber;
82
+ title: z.ZodString;
83
+ }, z.core.$strip>>;
84
+ }, z.core.$strip>;
85
+ export type Chapters = z.infer<typeof ChaptersSchema>;
86
+ export type ChapterMark = z.infer<typeof ChapterMarkSchema>;
87
+ /**
88
+ * One point on the cursor track. `x`/`y` are the centre of the step's `target`
89
+ * rect, in PlayRes (= viewport) pixels, so the same coordinates libass will draw at.
90
+ * `arriveMs` is when the cursor reaches the point — no later than the step's start, and
91
+ * pulled earlier for clicks so the arrow is parked and visible before it ripples — and
92
+ * `departMs` is when it may leave for the next point, pulled back from the step's finish
93
+ * to leave the glide room. `rippleMs` is when a click's ripple *starts*: the step's own
94
+ * start minus a measured 200ms lead, so the ripple is visibly under way before the
95
+ * screen the click changes — the step start rather than the action end because whatever
96
+ * `run()` awaits after the click is the change itself. Present only on `action: 'click'`
97
+ * points.
98
+ *
99
+ * Like chapter marks, these are stored rather than re-derived from `events/events.ndjson`
100
+ * at render time, so `captions/cursor.ass` is a pure function of the plan — which is also
101
+ * why retiming the derivation was not a compatibility event at all: an older reader
102
+ * executes the same frozen numbers over the same frozen ASS.
103
+ */
104
+ export declare const CursorPointSchema: z.ZodObject<{
105
+ id: z.ZodString;
106
+ x: z.ZodNumber;
107
+ y: z.ZodNumber;
108
+ arriveMs: z.ZodNumber;
109
+ departMs: z.ZodNumber;
110
+ action: z.ZodEnum<{
111
+ type: "type";
112
+ click: "click";
113
+ point: "point";
114
+ }>;
115
+ rippleMs: z.ZodOptional<z.ZodNumber>;
116
+ scale: z.ZodOptional<z.ZodNumber>;
117
+ }, z.core.$strip>;
118
+ export declare const CursorSchema: z.ZodObject<{
119
+ assPath: z.ZodLiteral<"captions/cursor.ass">;
120
+ points: z.ZodArray<z.ZodObject<{
121
+ id: z.ZodString;
122
+ x: z.ZodNumber;
123
+ y: z.ZodNumber;
124
+ arriveMs: z.ZodNumber;
125
+ departMs: z.ZodNumber;
126
+ action: z.ZodEnum<{
127
+ type: "type";
128
+ click: "click";
129
+ point: "point";
130
+ }>;
131
+ rippleMs: z.ZodOptional<z.ZodNumber>;
132
+ scale: z.ZodOptional<z.ZodNumber>;
133
+ }, z.core.$strip>>;
134
+ }, z.core.$strip>;
135
+ export type Cursor = z.infer<typeof CursorSchema>;
136
+ export type CursorPoint = z.infer<typeof CursorPointSchema>;
137
+ /**
138
+ * One camera window over the source frame. `enterMs` is when the ease INTO this
139
+ * shot starts; `holdFromMs` is when that ease ends and the shot is held. The window is
140
+ * then constant until the next shot's `enterMs`, which is why the list must *tile* the
141
+ * timeline rather than merely sort.
142
+ */
143
+ export declare const CameraShotSchema: z.ZodObject<{
144
+ id: z.ZodString;
145
+ enterMs: z.ZodNumber;
146
+ holdFromMs: z.ZodNumber;
147
+ x: z.ZodNumber;
148
+ y: z.ZodNumber;
149
+ w: z.ZodNumber;
150
+ h: z.ZodNumber;
151
+ }, z.core.$strip>;
152
+ /**
153
+ * The camera. A shot **list**, not frame-sampled
154
+ * keyframes: keyframing the window at 30 fps would add ~190 KB to a plan that is
155
+ * currently ~3 KB, for windows that hold constant for whole seconds. `render/camera.cmd`
156
+ * is generated from these shots at plan-freeze time and then frozen like every other
157
+ * command array, so the renderer stays a pure executor of arguments it does not
158
+ * recompute.
159
+ *
160
+ * The geometry rules below are measured, not stylistic (M10): on `yuv420p`, FFmpeg
161
+ * silently masks odd crop geometry down by a pixel, so a plan freezing an odd `x`/`y`/
162
+ * `w`/`h` would render a window different from the one it froze — the schema rejects
163
+ * loudly instead. `w % 32` is not a separate alignment rule: with `h` pinned to `9w/16`
164
+ * and 9 odd, `h` is even exactly when `w` is a multiple of 32, so it *is* the even-height
165
+ * rule, expressed on `w`. The ladder it implies — windows step down 1920, 1888, 1856, …,
166
+ * one rung ≈ 1.7% of zoom — is the measured granularity of the feature, not an accident
167
+ * to be trimmed.
168
+ */
169
+ export declare const CameraSchema: z.ZodObject<{
170
+ commandPath: z.ZodLiteral<"render/camera.cmd">;
171
+ shots: z.ZodArray<z.ZodObject<{
172
+ id: z.ZodString;
173
+ enterMs: z.ZodNumber;
174
+ holdFromMs: z.ZodNumber;
175
+ x: z.ZodNumber;
176
+ y: z.ZodNumber;
177
+ w: z.ZodNumber;
178
+ h: z.ZodNumber;
179
+ }, z.core.$strip>>;
180
+ }, z.core.$strip>;
181
+ export type Camera = z.infer<typeof CameraSchema>;
182
+ export type CameraShot = z.infer<typeof CameraShotSchema>;
183
+ /**
184
+ * One frozen narration clip and where it lands on the timeline.
185
+ *
186
+ * `durationMs` is stored rather than measured from the file, for the reason chapter marks
187
+ * store their own `endMs`: it makes `speech/narration.wav` a pure function of the plan, so
188
+ * nothing is re-derived at render time. It is also the number the *timeline* was built from
189
+ * — the recorder held the step open for it — so a file whose length disagrees means the
190
+ * bundle was edited after it was frozen, which is worth failing on rather than papering over.
191
+ *
192
+ * `source` distinguishes a synthesised clip from an author-supplied WAV, because they are the
193
+ * same bytes by the time they reach here and the distinction is not otherwise recoverable.
194
+ */
195
+ export declare const SpeechClipSchema: z.ZodObject<{
196
+ id: z.ZodString;
197
+ path: z.ZodString;
198
+ atMs: z.ZodNumber;
199
+ durationMs: z.ZodNumber;
200
+ source: z.ZodEnum<{
201
+ file: "file";
202
+ synth: "synth";
203
+ }>;
204
+ }, z.core.$strip>;
205
+ /**
206
+ * What produced the synthesised clips. Evidence only — nothing reads it to make a decision,
207
+ * exactly as `source.durationMs` is evidence only.
208
+ *
209
+ * It earns its place because it is the answer to the one question a frozen audio track cannot
210
+ * otherwise answer: *this bundle sounds different from that one, why?* The voice, the model
211
+ * bytes and the engine version are the three things that can change it, and none of them is
212
+ * recoverable from the WAV.
213
+ *
214
+ * Absent when every clip came from a file, which is why it is optional rather than filled
215
+ * with placeholders — an author-supplied voiceover has no model hash, and inventing one
216
+ * ("unknown", `0`.repeat(64)) would make the field lie in the one case it exists to explain.
217
+ */
218
+ export declare const SpeechEngineSchema: z.ZodObject<{
219
+ name: z.ZodString;
220
+ version: z.ZodString;
221
+ modelSha256: z.ZodString;
222
+ voice: z.ZodString;
223
+ }, z.core.$strip>;
224
+ /**
225
+ * Spoken narration. Absent means a silent video, which is every plan before this and
226
+ * the default after it.
227
+ *
228
+ * The clips are frozen *and* the mixed track is frozen. That is one more file than strictly
229
+ * needed, and it is deliberate: the clips are what the timeline was derived from — each one's
230
+ * length set its step's hold — so keeping them makes the bundle self-explanatory and lets
231
+ * `verify` hash them, while the mixed track is what FFmpeg muxes. Regenerating the mix from
232
+ * the clips is pure integer arithmetic (`narration.ts`), so the two can never disagree, and
233
+ * `renderBundle` reads the mix without regenerating anything.
234
+ */
235
+ export declare const SpeechSchema: z.ZodObject<{
236
+ trackPath: z.ZodLiteral<"speech/narration.wav">;
237
+ sampleRate: z.ZodLiteral<24000>;
238
+ channels: z.ZodLiteral<1>;
239
+ clips: z.ZodArray<z.ZodObject<{
240
+ id: z.ZodString;
241
+ path: z.ZodString;
242
+ atMs: z.ZodNumber;
243
+ durationMs: z.ZodNumber;
244
+ source: z.ZodEnum<{
245
+ file: "file";
246
+ synth: "synth";
247
+ }>;
248
+ }, z.core.$strip>>;
249
+ engine: z.ZodOptional<z.ZodObject<{
250
+ name: z.ZodString;
251
+ version: z.ZodString;
252
+ modelSha256: z.ZodString;
253
+ voice: z.ZodString;
254
+ }, z.core.$strip>>;
255
+ }, z.core.$strip>;
256
+ export type Speech = z.infer<typeof SpeechSchema>;
257
+ export type SpeechClip = z.infer<typeof SpeechClipSchema>;
258
+ /**
259
+ * The speech decision as the application layer supplies it; `trackPath`, `sampleRate` and
260
+ * `channels` are filled in by `buildRenderPlan`, exactly as `OutroInput` works.
261
+ */
262
+ export type SpeechInput = Omit<Speech, 'trackPath' | 'sampleRate' | 'channels'>;
263
+ /** The cursor decision as the application layer supplies it; `assPath` is filled in by
264
+ * `buildRenderPlan`, exactly as `OutroInput` and `ChaptersInput` work. */
265
+ export type CursorInput = Omit<Cursor, 'assPath'>;
266
+ /** The chapter decision as the application layer supplies it; `metadataPath` is filled in
267
+ * by `buildRenderPlan`, exactly as `OutroInput` works. */
268
+ export type ChaptersInput = {
269
+ marks: ChapterMark[];
270
+ };
271
+ /** The camera decision as the application layer supplies it; `commandPath` is filled in
272
+ * by `buildRenderPlan`, exactly as `CursorInput` and `ChaptersInput` work. */
273
+ export type CameraInput = Omit<Camera, 'commandPath'>;
274
+ /**
275
+ * The branding decision as the application layer supplies it: everything about the card
276
+ * except where its ASS file lives, which `buildRenderPlan` fills in. Declared here rather
277
+ * than in the renderer so that `@plaintake/license` can produce one without depending
278
+ * on the render path — the direction `renderer-no-license` forbids.
279
+ */
280
+ export type OutroInput = Omit<Outro, 'assPath'>;
281
+ /** The opening card as the application layer supplies it, exactly as `OutroInput` works. */
282
+ export type IntroInput = Omit<Intro, 'assPath'>;
283
+ /**
284
+ * The one value a plan's `schema` field ever holds.
285
+ *
286
+ * The field names the *document kind* — it is what makes a manifest or a result envelope
287
+ * handed to the renderer fail at the parse instead of three frames into an encode. The
288
+ * `/v1` suffix is frozen, not incremented: capabilities are stated by the plan's own
289
+ * optional fields, which is where they always were.
290
+ */
291
+ export declare const RENDER_PLAN_SCHEMA = "agent-demo.render/v1";
292
+ /**
293
+ * The frozen render contract. `ffmpeg` holds the exact argument arrays, which is what
294
+ * makes re-rendering a bundle bit-for-bit repeatable without re-deriving anything.
295
+ *
296
+ * **The version ladder this schema once carried has been retired.** It ran `/v1` (nothing) → `/v2`
297
+ * (outro) → `/v3` (chapters) → `/v4` (cursor) → `/v5` (camera), each rung the highest
298
+ * capability a plan used, stamped by `declaredVersion` and cross-checked by a refinement.
299
+ * Three things were wrong with it, and the third is why it went:
300
+ *
301
+ * 1. **It carried no information.** `declaredVersion` was a pure function of `outro`,
302
+ * `chapters`, `cursor` and `camera` — fields sitting in the same object — and the
303
+ * refinement asserted the stamp equalled that function. So the field was a cache of a
304
+ * derivation over its own neighbours, and the refinement was the cache-coherence check.
305
+ * `plan.camera !== undefined` is the capability test; `/v5` was a restatement of it.
306
+ * 2. **It encoded build order, not capability structure.** The four capabilities are
307
+ * independent booleans. A single integer cannot describe sixteen combinations, so `/v5`
308
+ * meant "a camera, and who knows what else" and every new feature had to become the new
309
+ * top rung by definition of having been built last.
310
+ * 3. **It cost forward compatibility rather than buying backward compatibility.** The
311
+ * backward direction — an old plan read by a new build — is carried by the fields being
312
+ * `.optional()`, and always was; the version string did nothing there. The forward
313
+ * direction is where the enum bit: `renderBundle` executes `plan.ffmpeg[variant]`
314
+ * verbatim and never regenerates the derived files, so a bundle from a newer build
315
+ * would have rendered *correctly* on an older one — the frozen arguments already name
316
+ * every input, and the files they name are already in the bundle. The enum refused it
317
+ * first. A marker justified as compatibility was the only thing preventing it.
318
+ *
319
+ * So a plan states its capabilities by carrying them. An unknown *field* is ignored, which
320
+ * is Zod's default and the right one — an older build ignoring `speech` still
321
+ * executes the frozen arguments that mux the frozen WAV. An unknown *document kind* is
322
+ * still rejected, because that is what the field is for.
323
+ *
324
+ * **`/v2`–`/v5` are not accepted.** They are gone rather than tolerated: this is pre-1.0
325
+ * software, a bundle is cheap to re-record, and a list of five strings where four exist only
326
+ * to be inert is the kind of thing that outlives the reason for it. A plan carrying one now
327
+ * fails at the parse, naming the value it should have — which is a better outcome than the
328
+ * ladder's, where the same bundle failed while carrying arguments that would have rendered.
329
+ *
330
+ * A plan using no optional capability remains byte-identical to what every earlier version
331
+ * produced, so `scripts/make-golden-bundle.sh` regenerates the committed golden bundle
332
+ * exactly — the version string was already `/v1` there.
333
+ */
334
+ export declare const RenderPlanSchema: z.ZodObject<{
335
+ schema: z.ZodLiteral<"agent-demo.render/v1">;
336
+ source: z.ZodObject<{
337
+ path: z.ZodLiteral<"raw/session.webm">;
338
+ sha256: z.ZodString;
339
+ durationMs: z.ZodNumber;
340
+ }, z.core.$strip>;
341
+ video: z.ZodObject<{
342
+ width: z.ZodLiteral<1920>;
343
+ height: z.ZodLiteral<1080>;
344
+ fps: z.ZodLiteral<30>;
345
+ pixelFormat: z.ZodLiteral<"yuv420p">;
346
+ durationMs: z.ZodNumber;
347
+ tailPadMs: z.ZodNumber;
348
+ }, z.core.$strip>;
349
+ captions: z.ZodObject<{
350
+ language: z.ZodString;
351
+ srtPath: z.ZodLiteral<"captions/captions.srt">;
352
+ vttPath: z.ZodLiteral<"captions/captions.vtt">;
353
+ assPath: z.ZodLiteral<"captions/captions.ass">;
354
+ cues: z.ZodArray<z.ZodObject<{
355
+ id: z.ZodString;
356
+ startMs: z.ZodNumber;
357
+ endMs: z.ZodNumber;
358
+ lines: z.ZodArray<z.ZodString>;
359
+ }, z.core.$strip>>;
360
+ }, z.core.$strip>;
361
+ style: z.ZodObject<{
362
+ fontFile: z.ZodLiteral<"assets/fonts/NotoSans-Regular.ttf">;
363
+ fontName: z.ZodLiteral<"Noto Sans">;
364
+ fontSize: z.ZodNumber;
365
+ textColor: z.ZodString;
366
+ outlineColor: z.ZodString;
367
+ outlineWidth: z.ZodNumber;
368
+ marginBottom: z.ZodNumber;
369
+ marginSide: z.ZodDefault<z.ZodNumber>;
370
+ borderStyle: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<4>]>>;
371
+ outlineOpacity: z.ZodDefault<z.ZodNumber>;
372
+ boxColor: z.ZodDefault<z.ZodString>;
373
+ boxOpacity: z.ZodDefault<z.ZodNumber>;
374
+ }, z.core.$strip>;
375
+ ffmpeg: z.ZodObject<{
376
+ base: z.ZodArray<z.ZodString>;
377
+ soft: z.ZodArray<z.ZodString>;
378
+ hard: z.ZodArray<z.ZodString>;
379
+ }, z.core.$strip>;
380
+ intro: z.ZodOptional<z.ZodObject<{
381
+ durationMs: z.ZodNumber;
382
+ lines: z.ZodArray<z.ZodString>;
383
+ backgroundColor: z.ZodString;
384
+ textColor: z.ZodString;
385
+ assPath: z.ZodLiteral<"captions/intro.ass">;
386
+ }, z.core.$strip>>;
387
+ outro: z.ZodOptional<z.ZodObject<{
388
+ durationMs: z.ZodNumber;
389
+ lines: z.ZodArray<z.ZodString>;
390
+ backgroundColor: z.ZodString;
391
+ textColor: z.ZodString;
392
+ assPath: z.ZodLiteral<"captions/outro.ass">;
393
+ }, z.core.$strip>>;
394
+ chapters: z.ZodOptional<z.ZodObject<{
395
+ metadataPath: z.ZodLiteral<"render/chapters.ffmetadata">;
396
+ marks: z.ZodArray<z.ZodObject<{
397
+ startMs: z.ZodNumber;
398
+ endMs: z.ZodNumber;
399
+ title: z.ZodString;
400
+ }, z.core.$strip>>;
401
+ }, z.core.$strip>>;
402
+ cursor: z.ZodOptional<z.ZodObject<{
403
+ assPath: z.ZodLiteral<"captions/cursor.ass">;
404
+ points: z.ZodArray<z.ZodObject<{
405
+ id: z.ZodString;
406
+ x: z.ZodNumber;
407
+ y: z.ZodNumber;
408
+ arriveMs: z.ZodNumber;
409
+ departMs: z.ZodNumber;
410
+ action: z.ZodEnum<{
411
+ type: "type";
412
+ click: "click";
413
+ point: "point";
414
+ }>;
415
+ rippleMs: z.ZodOptional<z.ZodNumber>;
416
+ scale: z.ZodOptional<z.ZodNumber>;
417
+ }, z.core.$strip>>;
418
+ }, z.core.$strip>>;
419
+ camera: z.ZodOptional<z.ZodObject<{
420
+ commandPath: z.ZodLiteral<"render/camera.cmd">;
421
+ shots: z.ZodArray<z.ZodObject<{
422
+ id: z.ZodString;
423
+ enterMs: z.ZodNumber;
424
+ holdFromMs: z.ZodNumber;
425
+ x: z.ZodNumber;
426
+ y: z.ZodNumber;
427
+ w: z.ZodNumber;
428
+ h: z.ZodNumber;
429
+ }, z.core.$strip>>;
430
+ }, z.core.$strip>>;
431
+ speech: z.ZodOptional<z.ZodObject<{
432
+ trackPath: z.ZodLiteral<"speech/narration.wav">;
433
+ sampleRate: z.ZodLiteral<24000>;
434
+ channels: z.ZodLiteral<1>;
435
+ clips: z.ZodArray<z.ZodObject<{
436
+ id: z.ZodString;
437
+ path: z.ZodString;
438
+ atMs: z.ZodNumber;
439
+ durationMs: z.ZodNumber;
440
+ source: z.ZodEnum<{
441
+ file: "file";
442
+ synth: "synth";
443
+ }>;
444
+ }, z.core.$strip>>;
445
+ engine: z.ZodOptional<z.ZodObject<{
446
+ name: z.ZodString;
447
+ version: z.ZodString;
448
+ modelSha256: z.ZodString;
449
+ voice: z.ZodString;
450
+ }, z.core.$strip>>;
451
+ }, z.core.$strip>>;
452
+ }, z.core.$strip>;
453
+ export type RenderPlan = z.infer<typeof RenderPlanSchema>;
454
+ export type Cue = z.infer<typeof CueSchema>;