@vanillaskyai/video 0.4.1 → 0.5.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/PUBLIC-API.md +12 -5
  3. package/README.md +13 -18
  4. package/dist/{builtin-server-BRTZN4Q7.js → builtin-server-YHEZ2JRF.js} +2 -2
  5. package/dist/check-runtime.js +1 -1
  6. package/dist/{chunk-BKF3A357.js → chunk-34O5BY6X.js} +1 -1
  7. package/dist/{chunk-LHFADNWJ.js → chunk-3ROGEU5A.js} +179 -342
  8. package/dist/{chunk-XAMUOSX7.js → chunk-3RV4YKB3.js} +0 -1
  9. package/dist/{chunk-GHJEAP4O.js → chunk-66MNRUCR.js} +1 -1
  10. package/dist/{chunk-EE6PK6XC.js → chunk-AOWSXU2K.js} +1 -1
  11. package/dist/{chunk-GR4I3MN2.js → chunk-EFL34TXF.js} +3 -3
  12. package/dist/{chunk-I5YWVQ5R.js → chunk-FVTMYS6U.js} +1 -1
  13. package/dist/{chunk-STIFILQG.js → chunk-FZHMQFG3.js} +46 -38
  14. package/dist/{chunk-JW47XCRL.js → chunk-G66Z5CWR.js} +30 -72
  15. package/dist/{chunk-RBQN3VSY.js → chunk-HVCPEAQF.js} +83 -1265
  16. package/dist/{chunk-N3KUA7VP.js → chunk-K6YVQODK.js} +4 -1
  17. package/dist/{chunk-WHPZED7X.js → chunk-PNS52FL4.js} +2 -5
  18. package/dist/{chunk-S5ZU5WUU.js → chunk-ZQQQAAKP.js} +2 -2
  19. package/dist/cli.js +20 -14
  20. package/dist/compose-video-QJHKTOQ7.js +13 -0
  21. package/dist/{events-D_1F6gw5.d.ts → events-BBP30j3c.d.ts} +3 -14
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.js +2 -2
  24. package/dist/{kit-DLeCr3d0.d.ts → kit-DEG3fcaL.d.ts} +1 -1
  25. package/dist/react.d.ts +6 -5
  26. package/dist/react.js +999 -348
  27. package/dist/server.d.ts +3 -3
  28. package/dist/server.js +18 -79
  29. package/dist/{system-prompt-NMYHLAMJ.js → system-prompt-4I6Z5HK3.js} +1 -1
  30. package/dist/template-catalog.js +1 -1
  31. package/dist/templates.d.ts +3 -3
  32. package/dist/templates.js +87 -4
  33. package/dist/test.d.ts +3 -27
  34. package/dist/test.js +3 -3
  35. package/dist/{text-stream-UPGUD2TD.js → text-stream-J6EDDJP4.js} +3 -3
  36. package/dist/{types-Cg0Onj7l.d.ts → types-BV9IqExh.d.ts} +1 -16
  37. package/docs/agent-integration.md +4 -3
  38. package/docs/concepts.md +4 -4
  39. package/docs/custom-templates.md +2 -2
  40. package/docs/getting-started.md +13 -18
  41. package/docs/integrate-nextjs.md +12 -8
  42. package/docs/persistence.md +1 -1
  43. package/docs/prompt-and-input.md +2 -2
  44. package/docs/reference/protocol.md +9 -12
  45. package/docs/reference/provider-adapters.md +5 -5
  46. package/docs/testing.md +1 -1
  47. package/examples/nextjs-quickstart/.env.example +2 -2
  48. package/examples/nextjs-quickstart/README.md +4 -1
  49. package/examples/nextjs-quickstart/package.json +4 -4
  50. package/examples/nextjs-quickstart/src/app/api/video/route.ts +4 -2
  51. package/package.json +8 -8
  52. package/dist/chunk-CR7FE7BW.js +0 -109
  53. package/dist/compose-video-6UQ33DOO.js +0 -13
@@ -4,24 +4,25 @@ import {
4
4
  import {
5
5
  applyVideoEvent,
6
6
  checksumVideo,
7
+ createReplayableStream,
7
8
  createVideoState
8
- } from "./chunk-STIFILQG.js";
9
+ } from "./chunk-FZHMQFG3.js";
9
10
  import {
10
11
  parseVideoPlanPart,
11
12
  safePublicDiagnostic
12
- } from "./chunk-JW47XCRL.js";
13
+ } from "./chunk-G66Z5CWR.js";
13
14
  import {
14
15
  MAX_RETAINED_INSTRUCTIONS_LENGTH,
15
16
  MAX_RETAINED_MEDIA_URLS,
16
17
  MAX_RETAINED_MEDIA_URL_LENGTH,
17
18
  MAX_RETAINED_SOURCE_LENGTH,
18
19
  parseVideo
19
- } from "./chunk-GHJEAP4O.js";
20
+ } from "./chunk-66MNRUCR.js";
20
21
  import {
21
22
  VIDEO_PROTOCOL_VERSION,
22
23
  VIDEO_SCHEMA_VERSION,
23
24
  createVideoRequest
24
- } from "./chunk-EE6PK6XC.js";
25
+ } from "./chunk-AOWSXU2K.js";
25
26
  import {
26
27
  getCloserReserve,
27
28
  getReadableSceneDuration,
@@ -84,12 +85,6 @@ function resolveSuppliedMediaPlanPart(part, input) {
84
85
  if (part.type === "scene.add") {
85
86
  return { ...part, scene: { ...part.scene, variables: resolveReferences(part.scene.variables, references) } };
86
87
  }
87
- if (part.type === "scene.patch" && part.patch.variables) {
88
- return { ...part, patch: { ...part.patch, variables: resolveReferences(part.patch.variables, references) } };
89
- }
90
- if (part.type === "asset.patch") {
91
- return { ...part, variables: resolveReferences(part.variables, references) };
92
- }
93
88
  return part;
94
89
  }
95
90
  function buildVideoUserPrompt(input, openingDurationSec = 0) {
@@ -100,7 +95,7 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
100
95
  typeof input.opening === "string" && input.opening.trim() ? `The host has already added the opening scene, which consumes ${openingDurationSec} seconds. Continue after it and do not repeat or rewrite it.` : "Add the first grounded scene as soon as it is complete.",
101
96
  "The first generated body scene must be fully playable without external media. Use a content-fit text, data, comparison, list, or device-free template with no media URL or keyword.",
102
97
  "Never use media, ctaMedia, or reaction as the first generated body template, even with mediaType=gradient. Choose a non-media template first.",
103
- "Add that scene before resolving any stock or supplied asset. Media belongs on later body scenes and must arrive without blocking scene additions.",
98
+ "Add that scene before resolving any stock or supplied asset. Resolve media as part of each complete later body scene.",
104
99
  "Use only claims supported by the factual basis permitted by the trusted system prompt.",
105
100
  "Select the most decision-relevant grounded takeaways that fit the duration; represent each selected takeaway once before completing the response.",
106
101
  "For a long source, summarize instead of attempting to represent every fact, unless the creative instructions explicitly request complete fact coverage that fits the duration.",
@@ -134,49 +129,51 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
134
129
  ].join("\n");
135
130
  }
136
131
 
137
- // src/server/compose-video.ts
132
+ // src/server/composition-input.ts
138
133
  var DEFAULT_OPENING_TEXT = "Creating your video...";
139
- function resolveVideoInput(input) {
134
+ function normalizeVideoInput(input) {
140
135
  return {
141
136
  ...input,
142
137
  opening: input.opening === false ? false : input.opening?.trim() || DEFAULT_OPENING_TEXT
143
138
  };
144
139
  }
145
140
  function resolveStreamCapabilities(capabilities, hasRuntimeOpening) {
146
- if (!hasRuntimeOpening) return capabilities;
147
- if (capabilities?.templates == null) return capabilities;
141
+ if (!hasRuntimeOpening || capabilities?.templates == null) return capabilities;
148
142
  return {
149
143
  ...capabilities,
150
144
  templates: [.../* @__PURE__ */ new Set(["media", ...capabilities.templates])]
151
145
  };
152
146
  }
153
- function invokeIsolated(callback, value) {
154
- if (!callback) return;
155
- try {
156
- void Promise.resolve(callback(value)).catch(() => void 0);
157
- } catch {
147
+ function validateVideoInput(input) {
148
+ if (!input.input.trim()) throw new Error("Video response input is required");
149
+ if (input.knowledgeMode != null && input.knowledgeMode !== "input-only" && input.knowledgeMode !== "general") {
150
+ throw new Error("Video response knowledge mode must be input-only or general");
158
151
  }
152
+ if (input.maxDurationSec != null && (!Number.isFinite(input.maxDurationSec) || input.maxDurationSec < 5 || input.maxDurationSec > 120)) {
153
+ throw new Error("Video response maximum duration must be between 5 and 120 seconds");
154
+ }
155
+ if (input.opening !== false && input.opening != null && !input.opening.trim()) {
156
+ throw new Error("Video response opening must be a non-empty string");
157
+ }
158
+ if (input.audio && !input.audio.src.trim()) {
159
+ throw new Error("Video response audio src must be a non-empty string");
160
+ }
161
+ if (input.audio && input.audio.src.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
162
+ throw new Error(`Video response audio src must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
163
+ }
164
+ for (const [index, media] of (input.suppliedMedia ?? []).entries()) {
165
+ if (media.url.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
166
+ throw new Error(`Video response supplied media ${index} URL must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
167
+ }
168
+ if (media.posterUrl && media.posterUrl.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
169
+ throw new Error(`Video response supplied media ${index} poster URL must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
170
+ }
171
+ }
172
+ resolveVideoBrand(input.brand);
159
173
  }
160
- function cloneWarning(warning) {
161
- return {
162
- code: warning.code,
163
- category: warning.category,
164
- message: warning.message,
165
- ...warning.sceneId != null ? { sceneId: warning.sceneId } : {},
166
- recoverable: warning.recoverable
167
- };
168
- }
169
- function now() {
170
- return globalThis.performance?.now?.() ?? Date.now();
171
- }
172
- function safeAbortReason(value) {
173
- if (value instanceof DOMException && value.name === "TimeoutError") return "Request timed out";
174
- if (value instanceof Error && value.name === "TimeoutError") return "Request timed out";
175
- return safePublicDiagnostic(value instanceof Error ? value.message : value, "Request aborted");
176
- }
177
- function buildInitialConfig(input, audio, snapshotRetention, closerReserveSec, getTemplatePacing) {
174
+ function buildInitialComposition(input, audio, snapshotRetention, closerReserveSec, getTemplatePacing) {
178
175
  const openingText = typeof input.opening === "string" ? input.opening.trim() : void 0;
179
- const rawScenes = openingText ? [{
176
+ const openingScenes = openingText ? [{
180
177
  id: "supplied-opening",
181
178
  templateId: "media",
182
179
  variables: {
@@ -187,7 +184,7 @@ function buildInitialConfig(input, audio, snapshotRetention, closerReserveSec, g
187
184
  }] : [];
188
185
  const scenes = [];
189
186
  const warnings = [];
190
- for (const scene of rawScenes) {
187
+ for (const scene of openingScenes) {
191
188
  const maxDurationSec = input.maxDurationSec ?? 30;
192
189
  const readableMinimum = getReadableSceneDuration(scene, getTemplatePacing?.(scene.templateId));
193
190
  if (maxDurationSec < readableMinimum) {
@@ -216,59 +213,83 @@ function buildInitialConfig(input, audio, snapshotRetention, closerReserveSec, g
216
213
  if (snapshotRetention?.suppliedMediaUrls && input.suppliedMedia?.length) {
217
214
  meta.uploadedMediaUrls = input.suppliedMedia.slice(0, MAX_RETAINED_MEDIA_URLS).map((item) => item.url);
218
215
  }
219
- return { config: {
220
- schemaVersion: VIDEO_SCHEMA_VERSION,
221
- orientation: input.orientation ?? "portrait",
222
- scenes,
223
- ...audio ? { audio } : {},
224
- style: {
225
- brand: resolveVideoBrand(input.brand),
226
- density: input.style?.density ?? "normal",
227
- motion: input.style?.motion ?? "normal",
228
- defaultBackgroundEffect: input.style?.backgroundEffect ?? "slow-zoom-in",
229
- defaultTextArchetype: input.style?.textArchetype ?? "subtle"
216
+ return {
217
+ config: {
218
+ schemaVersion: VIDEO_SCHEMA_VERSION,
219
+ orientation: input.orientation ?? "portrait",
220
+ scenes,
221
+ ...audio ? { audio } : {},
222
+ style: {
223
+ brand: resolveVideoBrand(input.brand),
224
+ density: input.style?.density ?? "normal",
225
+ motion: input.style?.motion ?? "normal",
226
+ defaultBackgroundEffect: input.style?.backgroundEffect ?? "slow-zoom-in",
227
+ defaultTextArchetype: input.style?.textArchetype ?? "subtle"
228
+ },
229
+ meta
230
230
  },
231
- meta
232
- }, warnings };
231
+ warnings
232
+ };
233
233
  }
234
- function validateInput(input) {
235
- if (!input.input.trim()) throw new Error("Video response input is required");
236
- if (input.knowledgeMode != null && input.knowledgeMode !== "input-only" && input.knowledgeMode !== "general") {
237
- throw new Error("Video response knowledge mode must be input-only or general");
238
- }
239
- if (input.maxDurationSec != null && (!Number.isFinite(input.maxDurationSec) || input.maxDurationSec < 5 || input.maxDurationSec > 120)) {
240
- throw new Error("Video response maximum duration must be between 5 and 120 seconds");
241
- }
242
- if (input.opening !== false && input.opening != null && !input.opening.trim()) {
243
- throw new Error("Video response opening must be a non-empty string");
244
- }
245
- if (input.audio && !input.audio.src.trim()) {
246
- throw new Error("Video response audio src must be a non-empty string");
247
- }
248
- if (input.audio && input.audio.src.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
249
- throw new Error(`Video response audio src must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
250
- }
251
- for (const [index, media] of (input.suppliedMedia ?? []).entries()) {
252
- if (media.url.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
253
- throw new Error(`Video response supplied media ${index} URL must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
254
- }
255
- if (media.posterUrl && media.posterUrl.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
256
- throw new Error(`Video response supplied media ${index} poster URL must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
257
- }
234
+
235
+ // src/server/composition-setup.ts
236
+ function prepareComposition(rawInput, options, requestId) {
237
+ validateVideoInput(rawInput);
238
+ const requiresGeneratedScene = rawInput.opening == null || rawInput.opening === false;
239
+ const input = normalizeVideoInput(rawInput);
240
+ const request = createVideoRequest(input, {
241
+ requestId,
242
+ capabilities: options.capabilities
243
+ });
244
+ const initialAudio = input.audio === false ? void 0 : input.audio ? {
245
+ trackId: "soundtrack",
246
+ audioUrl: input.audio.src,
247
+ duration: input.maxDurationSec ?? 30,
248
+ beatDetection: { sensitivity: 0.5 },
249
+ beatMarkers: [],
250
+ volume: 1,
251
+ fadeOutMs: 3e3
252
+ } : options.selectAudio?.(input);
253
+ const closerReserveSec = getCloserReserve(
254
+ options.capabilities?.templates,
255
+ options.getTemplatePacing
256
+ );
257
+ const initial = buildInitialComposition(
258
+ input,
259
+ initialAudio,
260
+ options.snapshotRetention,
261
+ closerReserveSec,
262
+ options.getTemplatePacing
263
+ );
264
+ const initialConfig = initial.config;
265
+ for (let position = 0; position < initialConfig.scenes.length; position += 1) {
266
+ options.validateScene?.(initialConfig.scenes[position], {
267
+ input,
268
+ previousScenes: initialConfig.scenes.slice(0, position)
269
+ });
258
270
  }
259
- resolveVideoBrand(input.brand);
260
- }
261
- function createId(prefix) {
262
- const randomId = globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
263
- return `${prefix}-${randomId}`;
271
+ return {
272
+ requiresGeneratedScene,
273
+ input,
274
+ request,
275
+ initial,
276
+ initialConfig,
277
+ userPrompt: buildVideoUserPrompt(
278
+ input,
279
+ initialConfig.scenes.at(-1)?.timing.endTime ?? 0
280
+ ),
281
+ closerReserveSec
282
+ };
264
283
  }
265
- function createPatchReadabilityWarning(sceneId) {
284
+
285
+ // src/server/composition-warnings.ts
286
+ function cloneWarning(warning) {
266
287
  return {
267
- code: "scene_patch_rejected_readability",
268
- category: "readability",
269
- message: "Scene update was omitted because it would make the timeline unreadable.",
270
- sceneId,
271
- recoverable: true
288
+ code: warning.code,
289
+ category: warning.category,
290
+ message: warning.message,
291
+ ...warning.sceneId != null ? { sceneId: warning.sceneId } : {},
292
+ recoverable: warning.recoverable
272
293
  };
273
294
  }
274
295
  function createOmittedForCloserWarning(sceneId) {
@@ -311,9 +332,6 @@ function pendingCloserReserve(scene, getTemplatePacing) {
311
332
  const requested = explicitRange ?? scene.timing.fixedDuration ?? metadata?.preferredDuration ?? readableMinimum;
312
333
  return Math.max(readableMinimum, requested);
313
334
  }
314
- function sceneSlotDuration(scene) {
315
- return Math.max(0, (scene.timing.endTime ?? 0) - (scene.timing.startTime ?? 0));
316
- }
317
335
  function createSceneQualityWarnings(scene) {
318
336
  if (scene.templateId !== "barChart" || !Array.isArray(scene.variables.bars)) return [];
319
337
  const values = scene.variables.bars.flatMap((bar) => {
@@ -330,47 +348,63 @@ function createSceneQualityWarnings(scene) {
330
348
  recoverable: true
331
349
  }];
332
350
  }
351
+
352
+ // src/server/composition-runtime.ts
353
+ function invokeIsolated(callback, value) {
354
+ if (!callback) return;
355
+ try {
356
+ void Promise.resolve(callback(value)).catch(() => void 0);
357
+ } catch {
358
+ }
359
+ }
360
+ function monotonicNow() {
361
+ return globalThis.performance?.now?.() ?? Date.now();
362
+ }
363
+ function safeAbortReason(value) {
364
+ if (value instanceof DOMException && value.name === "TimeoutError") return "Request timed out";
365
+ if (value instanceof Error && value.name === "TimeoutError") return "Request timed out";
366
+ return safePublicDiagnostic(value instanceof Error ? value.message : value, "Request aborted");
367
+ }
368
+ function createId(prefix) {
369
+ const randomId = globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
370
+ return `${prefix}-${randomId}`;
371
+ }
372
+ function createProviderLifecycle() {
373
+ const results = [];
374
+ const sink = {
375
+ registerProviderResult(result) {
376
+ results.push(result);
377
+ }
378
+ };
379
+ const settle = async () => {
380
+ const resolved = await Promise.all(results);
381
+ const warnings = resolved.flatMap((result) => result.warnings);
382
+ const uniqueWarnings = [...new Map(warnings.map((warning) => [
383
+ `${warning.code}\0${warning.category}\0${warning.message}\0${warning.sceneId ?? ""}`,
384
+ warning
385
+ ])).values()];
386
+ return resolved.reduce((summary, result) => ({
387
+ ...summary,
388
+ ...result,
389
+ warnings: uniqueWarnings
390
+ }), { warnings: uniqueWarnings });
391
+ };
392
+ return { sink, settle };
393
+ }
394
+
395
+ // src/server/compose-video.ts
333
396
  function createVideo(rawInput, options) {
334
- validateInput(rawInput);
335
- const requiresGeneratedScene = rawInput.opening == null || rawInput.opening === false;
336
- const input = resolveVideoInput(rawInput);
337
397
  const requestId = options.requestId ?? createId("request");
338
398
  const runId = options.runId ?? createId("run");
339
- const request = createVideoRequest(input, {
340
- requestId,
341
- capabilities: options.capabilities
342
- });
343
- const initialAudio = input.audio === false ? void 0 : input.audio ? {
344
- trackId: "soundtrack",
345
- audioUrl: input.audio.src,
346
- duration: input.maxDurationSec ?? 30,
347
- beatDetection: { sensitivity: 0.5 },
348
- beatMarkers: [],
349
- volume: 1,
350
- fadeOutMs: 3e3
351
- } : options.selectAudio?.(input);
352
- const closerReserveSec = getCloserReserve(
353
- options.capabilities?.templates,
354
- options.getTemplatePacing
355
- );
356
- const initial = buildInitialConfig(
399
+ const {
400
+ requiresGeneratedScene,
357
401
  input,
358
- initialAudio,
359
- options.snapshotRetention,
360
- closerReserveSec,
361
- options.getTemplatePacing
362
- );
363
- const initialConfig = initial.config;
364
- for (let position = 0; position < initialConfig.scenes.length; position += 1) {
365
- options.validateScene?.(initialConfig.scenes[position], {
366
- input,
367
- previousScenes: initialConfig.scenes.slice(0, position)
368
- });
369
- }
370
- const userPrompt = buildVideoUserPrompt(
371
- input,
372
- initialConfig.scenes.at(-1)?.timing.endTime ?? 0
373
- );
402
+ request,
403
+ initial,
404
+ initialConfig,
405
+ userPrompt,
406
+ closerReserveSec
407
+ } = prepareComposition(rawInput, options, requestId);
374
408
  const events = createVideoEventFactory({ runId });
375
409
  const controller = new AbortController();
376
410
  let abortReason = "Request aborted";
@@ -387,35 +421,17 @@ function createVideo(rawInput, options) {
387
421
  resolveResult = resolve;
388
422
  rejectResult = reject;
389
423
  });
390
- const startedAt = now();
424
+ const startedAt = monotonicNow();
391
425
  let acceptedSceneCount = 0;
392
426
  let rejectedSceneCount = 0;
393
427
  let timeToFirstSceneMs;
394
- const providerResults = [];
395
- const lifecycle = {
396
- registerProviderResult(providerResult) {
397
- providerResults.push(providerResult);
398
- }
399
- };
428
+ const { sink: lifecycle, settle: settleProviderLifecycle } = createProviderLifecycle();
400
429
  const reportedErrors = /* @__PURE__ */ new WeakSet();
401
430
  const reportError = (error) => {
402
431
  if (reportedErrors.has(error)) return;
403
432
  reportedErrors.add(error);
404
433
  invokeIsolated(options.onError, error);
405
434
  };
406
- const settleProviderLifecycle = async () => {
407
- const results = await Promise.all(providerResults);
408
- const warnings = results.flatMap((result2) => result2.warnings);
409
- const uniqueWarnings = [...new Map(warnings.map((warning) => [
410
- `${warning.code}\0${warning.category}\0${warning.message}\0${warning.sceneId ?? ""}`,
411
- warning
412
- ])).values()];
413
- return results.reduce((summary, result2) => ({
414
- ...summary,
415
- ...result2,
416
- warnings: uniqueWarnings
417
- }), { warnings: uniqueWarnings });
418
- };
419
435
  const eventSource = (async function* () {
420
436
  let state = createVideoState();
421
437
  const emit = (event) => {
@@ -456,11 +472,10 @@ function createVideo(rawInput, options) {
456
472
  for (let position = 0; position < initialConfig.scenes.length; position += 1) {
457
473
  yield emit(events.create("scene.add", {
458
474
  scene: initialConfig.scenes[position],
459
- position,
460
- revision: 0
475
+ position
461
476
  }));
462
477
  }
463
- const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-NMYHLAMJ.js")).createVideoSystemPrompt(input.knowledgeMode);
478
+ const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-4I6Z5HK3.js")).createVideoSystemPrompt(input.knowledgeMode);
464
479
  const context = { request, systemPrompt, userPrompt, initialConfig, signal: controller.signal };
465
480
  attachGenerationLifecycleSink(context, lifecycle);
466
481
  for await (const untrustedPart of options.generate(context)) {
@@ -530,154 +545,11 @@ function createVideo(rawInput, options) {
530
545
  }
531
546
  yield emit(events.create("scene.add", {
532
547
  scene,
533
- position: state.config?.scenes.length ?? 0,
534
- revision: 0
548
+ position: state.config?.scenes.length ?? 0
535
549
  }));
536
550
  generatedSceneCount += 1;
537
551
  acceptedSceneCount += 1;
538
- timeToFirstSceneMs ??= Math.max(0, now() - startedAt);
539
- } else if (part.type === "scene.patch") {
540
- if (pendingCloser?.id === part.sceneId) {
541
- const nextPendingCloser = {
542
- ...pendingCloser,
543
- ...part.patch,
544
- variables: part.patch.variables ? { ...pendingCloser.variables, ...part.patch.variables } : pendingCloser.variables,
545
- timing: part.patch.timing ? { ...pendingCloser.timing, ...part.patch.timing } : pendingCloser.timing
546
- };
547
- options.validateScene?.(nextPendingCloser, {
548
- input,
549
- previousScenes: state.config?.scenes ?? []
550
- });
551
- pendingCloser = nextPendingCloser;
552
- continue;
553
- }
554
- const scenes = state.config?.scenes ?? [];
555
- const sceneIndex = scenes.findIndex((scene) => scene.id === part.sceneId);
556
- if (sceneIndex < 0) throw new Error(`Scene ${part.sceneId} does not exist`);
557
- if (part.patch.timing && sceneIndex < scenes.length - 1) {
558
- yield emit(events.create("response.warning", {
559
- warning: createPatchReadabilityWarning(part.sceneId)
560
- }));
561
- continue;
562
- }
563
- const currentScene = scenes[sceneIndex];
564
- if (part.patch.timing?.startTime != null && Math.abs(part.patch.timing.startTime - (currentScene.timing.startTime ?? 0)) > 1e-6) {
565
- yield emit(events.create("response.warning", {
566
- warning: createPatchReadabilityWarning(part.sceneId)
567
- }));
568
- continue;
569
- }
570
- let timing;
571
- if (part.patch.timing) {
572
- const requestedTiming = { ...currentScene.timing, ...part.patch.timing };
573
- if (part.patch.timing.startTime != null && part.patch.timing.endTime == null) {
574
- delete requestedTiming.endTime;
575
- }
576
- if (part.patch.timing.fixedDuration != null && part.patch.timing.endTime == null) {
577
- delete requestedTiming.endTime;
578
- }
579
- const pacedPatch = paceScene({
580
- ...currentScene,
581
- ...part.patch,
582
- variables: part.patch.variables ? { ...currentScene.variables, ...part.patch.variables } : currentScene.variables,
583
- timing: requestedTiming
584
- }, {
585
- previousScenes: scenes.slice(0, sceneIndex),
586
- audio: state.config?.audio,
587
- maxDurationSec: input.maxDurationSec ?? 30,
588
- closerReserveSec: pendingCloser ? pendingCloserReserve(pendingCloser, options.getTemplatePacing) : closerReserveSec,
589
- getTemplatePacing: options.getTemplatePacing
590
- });
591
- if (!pacedPatch.scene) {
592
- yield emit(events.create("response.warning", {
593
- warning: createPatchReadabilityWarning(part.sceneId)
594
- }));
595
- continue;
596
- }
597
- for (const warning of pacedPatch.warnings) {
598
- yield emit(events.create("response.warning", { warning }));
599
- }
600
- timing = pacedPatch.scene.timing;
601
- }
602
- const patch = {
603
- ...part.patch,
604
- ...timing ? { timing } : {}
605
- };
606
- const nextScene = {
607
- ...currentScene,
608
- ...patch,
609
- variables: patch.variables ? { ...currentScene.variables, ...patch.variables } : currentScene.variables,
610
- timing: patch.timing ? { ...currentScene.timing, ...patch.timing } : currentScene.timing
611
- };
612
- options.validateScene?.(nextScene, { input, previousScenes: scenes });
613
- if (getReadableSceneDuration(
614
- nextScene,
615
- options.getTemplatePacing?.(nextScene.templateId)
616
- ) > sceneSlotDuration(nextScene) + 1e-6) {
617
- yield emit(events.create("response.warning", {
618
- warning: createPatchReadabilityWarning(part.sceneId)
619
- }));
620
- continue;
621
- }
622
- yield emit(events.create("scene.patch", {
623
- sceneId: part.sceneId,
624
- revision: (state.sceneRevisions[part.sceneId] ?? -1) + 1,
625
- patch
626
- }));
627
- } else if (part.type === "asset.patch") {
628
- if (pendingCloser?.id === part.sceneId) {
629
- const nextPendingCloser = {
630
- ...pendingCloser,
631
- variables: { ...pendingCloser.variables, ...part.variables }
632
- };
633
- options.validateScene?.(nextPendingCloser, {
634
- input,
635
- previousScenes: state.config?.scenes ?? []
636
- });
637
- pendingCloser = nextPendingCloser;
638
- continue;
639
- }
640
- const scenes = state.config?.scenes ?? [];
641
- const scene = scenes.find((item) => item.id === part.sceneId);
642
- if (!scene) throw new Error(`Scene ${part.sceneId} does not exist`);
643
- const nextScene = {
644
- ...scene,
645
- variables: { ...scene.variables, ...part.variables }
646
- };
647
- options.validateScene?.(nextScene, { input, previousScenes: scenes });
648
- if (getReadableSceneDuration(
649
- nextScene,
650
- options.getTemplatePacing?.(nextScene.templateId)
651
- ) > sceneSlotDuration(nextScene) + 1e-6) {
652
- yield emit(events.create("response.warning", {
653
- warning: createPatchReadabilityWarning(part.sceneId)
654
- }));
655
- continue;
656
- }
657
- yield emit(events.create("asset.patch", {
658
- sceneId: part.sceneId,
659
- revision: (state.sceneRevisions[part.sceneId] ?? -1) + 1,
660
- variables: part.variables
661
- }));
662
- } else if (part.type === "plan.error") {
663
- const internalError = new Error(part.error.message);
664
- reportError(internalError);
665
- const terminal = !(part.error.recoverable ?? false);
666
- const errorEvent = emit(events.create("response.error", {
667
- error: {
668
- code: "generation_failed",
669
- message: "Video response generation failed",
670
- recoverable: part.error.recoverable ?? false
671
- },
672
- terminal,
673
- ...terminal && state.config ? { snapshot: state.config } : {}
674
- }));
675
- if (terminal) {
676
- finish(state);
677
- yield errorEvent;
678
- return;
679
- }
680
- yield errorEvent;
552
+ timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
681
553
  } else if (part.type === "plan.complete") {
682
554
  if (planCompleted) throw new Error("The planner emitted plan.complete more than once");
683
555
  if (pendingCloser) {
@@ -701,12 +573,11 @@ function createVideo(rawInput, options) {
701
573
  if (pacedCloser.scene) {
702
574
  yield emit(events.create("scene.add", {
703
575
  scene: pacedCloser.scene,
704
- position: state.config?.scenes.length ?? 0,
705
- revision: 0
576
+ position: state.config?.scenes.length ?? 0
706
577
  }));
707
578
  generatedSceneCount += 1;
708
579
  acceptedSceneCount += 1;
709
- timeToFirstSceneMs ??= Math.max(0, now() - startedAt);
580
+ timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
710
581
  closerCommitted = true;
711
582
  } else {
712
583
  runtimeDurationLimited = true;
@@ -734,12 +605,11 @@ function createVideo(rawInput, options) {
734
605
  }
735
606
  yield emit(events.create("scene.add", {
736
607
  scene: recovered.scene,
737
- position: state.config?.scenes.length ?? 0,
738
- revision: 0
608
+ position: state.config?.scenes.length ?? 0
739
609
  }));
740
610
  generatedSceneCount += 1;
741
611
  acceptedSceneCount += 1;
742
- timeToFirstSceneMs ??= Math.max(0, now() - startedAt);
612
+ timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
743
613
  }
744
614
  }
745
615
  if (options.requireCloser && !closerCommitted) {
@@ -802,7 +672,7 @@ function createVideo(rawInput, options) {
802
672
  ...provider.requestedModelId ? { requestedModelId: provider.requestedModelId } : {},
803
673
  ...provider.resolvedModelId ? { resolvedModelId: provider.resolvedModelId } : {},
804
674
  ...timeToFirstSceneMs != null ? { timeToFirstSceneMs } : {},
805
- totalDurationMs: Math.max(0, now() - startedAt),
675
+ totalDurationMs: Math.max(0, monotonicNow() - startedAt),
806
676
  acceptedSceneCount,
807
677
  rejectedSceneCount,
808
678
  videoDurationSec: snapshot.scenes.at(-1)?.timing.endTime ?? 0,
@@ -835,8 +705,7 @@ function createVideo(rawInput, options) {
835
705
  if (pacedCloser.scene) {
836
706
  yield emit(events.create("scene.add", {
837
707
  scene: pacedCloser.scene,
838
- position: state.config.scenes.length,
839
- revision: 0
708
+ position: state.config.scenes.length
840
709
  }));
841
710
  generatedSceneCount += 1;
842
711
  acceptedSceneCount += 1;
@@ -874,44 +743,12 @@ function createVideo(rawInput, options) {
874
743
  options.signal?.removeEventListener("abort", forwardAbort);
875
744
  }
876
745
  })();
877
- const buffered = [];
878
- const waiters = /* @__PURE__ */ new Set();
879
- let streamDone = false;
880
- let streamError;
881
- const notify = () => {
882
- for (const waiter of waiters) waiter();
883
- waiters.clear();
884
- };
885
- void (async () => {
886
- try {
887
- for await (const event of eventSource) {
888
- buffered.push(event);
889
- notify();
890
- }
891
- } catch (cause) {
892
- streamError = cause;
893
- if (!settled) {
894
- settled = true;
895
- rejectResult(cause);
896
- }
897
- } finally {
898
- streamDone = true;
899
- notify();
746
+ const stream = createReplayableStream(eventSource, (cause) => {
747
+ if (!settled) {
748
+ settled = true;
749
+ rejectResult(cause);
900
750
  }
901
- })();
902
- const stream = {
903
- async *[Symbol.asyncIterator]() {
904
- let index = 0;
905
- while (true) {
906
- while (index < buffered.length) yield buffered[index++];
907
- if (streamDone) {
908
- if (streamError) throw streamError;
909
- return;
910
- }
911
- await new Promise((resolve) => waiters.add(resolve));
912
- }
913
- }
914
- };
751
+ });
915
752
  return {
916
753
  request,
917
754
  initialConfig,