@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
package/dist/server.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { n as VideoGenerationContext, o as VideoPlanner, p as VideoRequest, a as VideoCapabilities, j as VideoInput, d as VideoAudio, q as VideoSceneValidator, r as VideoTemplatePacing, s as VideoSnapshotRetention, t as VideoResumeCursor, u as VideoSceneValidationContext, e as VideoScene } from './types-Cg0Onj7l.js';
2
- import { b as VideoFinishReason, a as VideoWarning, V as VideoEvent } from './events-D_1F6gw5.js';
3
- export { c as VideoWarningCategory } from './events-D_1F6gw5.js';
1
+ import { n as VideoGenerationContext, o as VideoPlanner, p as VideoRequest, a as VideoCapabilities, j as VideoInput, d as VideoAudio, q as VideoSceneValidator, r as VideoTemplatePacing, s as VideoSnapshotRetention, t as VideoResumeCursor, u as VideoSceneValidationContext, e as VideoScene } from './types-BV9IqExh.js';
2
+ import { a as VideoFinishReason, V as VideoWarning, b as VideoEvent } from './events-BBP30j3c.js';
3
+ export { c as VideoWarningCategory } from './events-BBP30j3c.js';
4
4
  import { S as SceneTemplateMetadata } from './catalog-types-BIhSpOWK.js';
5
5
 
6
6
  interface VideoProviderUsage {
package/dist/server.js CHANGED
@@ -1,41 +1,46 @@
1
1
  import {
2
2
  createTextDeltaVideoPlanner
3
- } from "./chunk-BKF3A357.js";
3
+ } from "./chunk-34O5BY6X.js";
4
4
  import {
5
5
  createVideo
6
- } from "./chunk-LHFADNWJ.js";
6
+ } from "./chunk-3ROGEU5A.js";
7
7
  import "./chunk-E7CL7UPB.js";
8
8
  import {
9
9
  BUILTIN_SERVER_TEMPLATE_KIT,
10
10
  createServerTemplateRegistry,
11
11
  overlayServerTemplateRegistry
12
- } from "./chunk-I5YWVQ5R.js";
12
+ } from "./chunk-FVTMYS6U.js";
13
13
  import {
14
14
  createTemplateSceneValidator
15
15
  } from "./chunk-LMG3GHLL.js";
16
16
  import {
17
17
  encodeVideoSseEvent,
18
18
  videoSseHeaders
19
- } from "./chunk-S5ZU5WUU.js";
20
- import "./chunk-STIFILQG.js";
19
+ } from "./chunk-ZQQQAAKP.js";
20
+ import "./chunk-FZHMQFG3.js";
21
21
  import {
22
+ allowedKeys,
23
+ jsonValue,
24
+ nonNegativeInteger,
22
25
  parseVideoEvent,
23
- safePublicDiagnostic
24
- } from "./chunk-JW47XCRL.js";
26
+ record,
27
+ safePublicDiagnostic,
28
+ string
29
+ } from "./chunk-G66Z5CWR.js";
25
30
  import {
26
31
  MAX_RETAINED_MEDIA_URL_LENGTH
27
- } from "./chunk-GHJEAP4O.js";
32
+ } from "./chunk-66MNRUCR.js";
28
33
  import {
29
34
  VIDEO_PROTOCOL_VERSION
30
- } from "./chunk-EE6PK6XC.js";
35
+ } from "./chunk-AOWSXU2K.js";
31
36
  import {
32
37
  createTemplateSystemPrompt,
33
38
  getStandardMediaResolverContract
34
- } from "./chunk-GR4I3MN2.js";
35
- import "./chunk-WHPZED7X.js";
39
+ } from "./chunk-EFL34TXF.js";
40
+ import "./chunk-PNS52FL4.js";
36
41
  import "./chunk-3O7OMMMF.js";
42
+ import "./chunk-3RV4YKB3.js";
37
43
  import "./chunk-352ZZCET.js";
38
- import "./chunk-XAMUOSX7.js";
39
44
  import "./chunk-2E6T633S.js";
40
45
  import {
41
46
  validateBrandInput
@@ -43,23 +48,6 @@ import {
43
48
  import "./chunk-73NTSFFI.js";
44
49
 
45
50
  // src/server/request-validation.ts
46
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
47
- function record(value, path) {
48
- if (!value || typeof value !== "object" || Array.isArray(value)) {
49
- throw new Error(`${path} must be an object`);
50
- }
51
- return value;
52
- }
53
- function allowedKeys(value, allowed, path) {
54
- const permitted = new Set(allowed);
55
- for (const key of Object.keys(value)) {
56
- if (!permitted.has(key)) throw new Error(`${path} contains unsupported field ${key}`);
57
- }
58
- }
59
- function string(value, path) {
60
- if (typeof value !== "string" || !value.trim()) throw new Error(`${path} must be a non-empty string`);
61
- return value;
62
- }
63
51
  function boundedString(value, path) {
64
52
  const result = string(value, path);
65
53
  if (result.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
@@ -67,12 +55,6 @@ function boundedString(value, path) {
67
55
  }
68
56
  return result;
69
57
  }
70
- function nonNegativeInteger(value, path) {
71
- if (!Number.isInteger(value) || Number(value) < 0) {
72
- throw new Error(`${path} must be a non-negative integer`);
73
- }
74
- return Number(value);
75
- }
76
58
  function numberBetween(value, minimum, maximum, path) {
77
59
  if (typeof value !== "number" || !Number.isFinite(value) || value < minimum || value > maximum) {
78
60
  throw new Error(`${path} must be a number between ${minimum} and ${maximum}`);
@@ -85,24 +67,6 @@ function enumValue(value, allowed, description, path) {
85
67
  }
86
68
  return value;
87
69
  }
88
- function jsonValue(value, path, seen = /* @__PURE__ */ new Set()) {
89
- if (value == null || typeof value === "string" || typeof value === "boolean") return;
90
- if (typeof value === "number") {
91
- if (!Number.isFinite(value)) throw new Error(`${path} must be finite`);
92
- return;
93
- }
94
- if (typeof value !== "object" || seen.has(value)) throw new Error(`${path} must be JSON-serializable`);
95
- seen.add(value);
96
- if (Array.isArray(value)) {
97
- value.forEach((item, index) => jsonValue(item, `${path}[${index}]`, seen));
98
- } else {
99
- for (const [key, child] of Object.entries(value)) {
100
- if (DANGEROUS_KEYS.has(key)) throw new Error(`${path} contains unsupported field ${key}`);
101
- jsonValue(child, `${path}.${key}`, seen);
102
- }
103
- }
104
- seen.delete(value);
105
- }
106
70
  function capabilities(value, path) {
107
71
  const result = record(value, path);
108
72
  allowedKeys(result, ["templates", "extensions"], path);
@@ -553,12 +517,6 @@ function sanitizeUnknownTemplatePart(part) {
553
517
  if (part.type === "scene.add") {
554
518
  return { ...part, scene: { ...part.scene, variables: withoutKeyword(part.scene.variables) } };
555
519
  }
556
- if (part.type === "scene.patch" && part.patch.variables) {
557
- return { ...part, patch: { ...part.patch, variables: withoutKeyword(part.patch.variables) } };
558
- }
559
- if (part.type === "asset.patch") {
560
- return { ...part, variables: withoutKeyword(part.variables) };
561
- }
562
520
  return part;
563
521
  }
564
522
  async function resolvePartVariables(options) {
@@ -579,33 +537,14 @@ async function resolvePartVariables(options) {
579
537
  });
580
538
  return { ...options.part, scene: { ...options.part.scene, variables } };
581
539
  }
582
- if (options.part.type === "scene.patch" && options.part.patch.variables) {
583
- const variables = await resolveVariables({
584
- ...shared,
585
- variables: options.part.patch.variables
586
- });
587
- return { ...options.part, patch: { ...options.part.patch, variables } };
588
- }
589
- if (options.part.type === "asset.patch") {
590
- const variables = await resolveVariables({
591
- ...shared,
592
- variables: options.part.variables
593
- });
594
- return { ...options.part, variables };
595
- }
596
540
  return options.part;
597
541
  }
598
542
  function createMediaResolvingPlanner(options) {
599
543
  return async function* resolveMediaPlan(context) {
600
- const sceneTemplates = /* @__PURE__ */ new Map();
601
544
  for await (const part of options.planner(context)) {
602
- const templateId = part.type === "scene.add" ? part.scene.templateId : part.type === "scene.patch" || part.type === "asset.patch" ? sceneTemplates.get(part.sceneId) : void 0;
603
- if (part.type === "scene.add") {
604
- sceneTemplates.set(part.scene.id, part.scene.templateId);
605
- }
606
545
  yield await resolvePartVariables({
607
546
  part,
608
- templateId,
547
+ templateId: part.type === "scene.add" ? part.scene.templateId : void 0,
609
548
  input: context.request.input,
610
549
  signal: context.signal,
611
550
  templates: options.templates,
@@ -2,7 +2,7 @@ import {
2
2
  DEFAULT_VIDEO_SYSTEM_PROMPT,
3
3
  VIDEO_PLAN_INSTRUCTION,
4
4
  createVideoSystemPrompt
5
- } from "./chunk-WHPZED7X.js";
5
+ } from "./chunk-PNS52FL4.js";
6
6
  export {
7
7
  DEFAULT_VIDEO_SYSTEM_PROMPT,
8
8
  VIDEO_PLAN_INSTRUCTION,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BUILTIN_TEMPLATE_MANIFEST
3
- } from "./chunk-RBQN3VSY.js";
3
+ } from "./chunk-HVCPEAQF.js";
4
4
  export {
5
5
  BUILTIN_TEMPLATE_MANIFEST as builtinTemplates
6
6
  };
@@ -1,9 +1,9 @@
1
- import { I as InferTemplateJsonSchema, S as SceneTemplateProps, a as SceneTemplate } from './kit-DLeCr3d0.js';
2
- export { T as TemplateRegistry, c as createTemplateRegistry } from './kit-DLeCr3d0.js';
1
+ import { I as InferTemplateJsonSchema, S as SceneTemplateProps, a as SceneTemplate } from './kit-DEG3fcaL.js';
2
+ export { T as TemplateRegistry, c as createTemplateRegistry } from './kit-DEG3fcaL.js';
3
3
  import { ComponentType } from 'react';
4
4
  import { d as TemplateJsonSchema, S as SceneTemplateMetadata } from './catalog-types-BIhSpOWK.js';
5
5
  export { T as TemplateFamily, c as TemplateTimingMetadata, e as TemplateTransitionTiming } from './catalog-types-BIhSpOWK.js';
6
- import './types-Cg0Onj7l.js';
6
+ import './types-BV9IqExh.js';
7
7
 
8
8
  interface TemplateExample<Variables extends Record<string, unknown> = Record<string, unknown>> {
9
9
  name: string;
package/dist/templates.js CHANGED
@@ -1,8 +1,91 @@
1
1
  import {
2
- createTemplateRegistry,
3
- defineTemplate
4
- } from "./chunk-CR7FE7BW.js";
5
- import "./chunk-2E6T633S.js";
2
+ assertTemplateTransitionMetadata
3
+ } from "./chunk-2E6T633S.js";
4
+
5
+ // src/visual-system/catalog/kit.ts
6
+ function toMetadata(template) {
7
+ return {
8
+ id: template.id,
9
+ label: template.label,
10
+ description: template.description,
11
+ category: template.category,
12
+ family: template.family,
13
+ usesGlobalTextEffect: template.usesGlobalTextEffect,
14
+ usesGlobalTransition: template.usesGlobalTransition,
15
+ transitionTiming: template.transitionTiming,
16
+ usesGlobalBackgroundEffect: template.usesGlobalBackgroundEffect,
17
+ textCanvas: template.textCanvas,
18
+ jobs: template.jobs,
19
+ register: template.register,
20
+ useWhen: template.useWhen,
21
+ avoidWhen: template.avoidWhen,
22
+ schema: template.schema,
23
+ minDuration: template.minDuration,
24
+ preferredDuration: template.preferredDuration,
25
+ timing: template.timing
26
+ };
27
+ }
28
+ function isTemplateJsonSchema(value) {
29
+ if (typeof value !== "object" || value == null) return false;
30
+ const schema = value;
31
+ return schema.type === "object" && typeof schema.properties === "object" && schema.properties != null;
32
+ }
33
+ function defineTemplate(definition) {
34
+ if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(definition.id)) {
35
+ throw new Error(`Invalid template id: ${definition.id}`);
36
+ }
37
+ const suppliedSchema = "schema" in definition ? definition.schema : void 0;
38
+ const standard = suppliedSchema && "~standard" in suppliedSchema ? suppliedSchema["~standard"] : void 0;
39
+ const convertedSchema = standard?.jsonSchema ? standard.jsonSchema.input({ target: "draft-07" }) : suppliedSchema && !("~standard" in suppliedSchema) ? suppliedSchema : void 0;
40
+ if (standard && !standard.jsonSchema) {
41
+ throw new Error("Template schemas must expose Standard JSON Schema so the server registry can be generated safely");
42
+ }
43
+ if (!convertedSchema || !isTemplateJsonSchema(convertedSchema)) {
44
+ throw new Error("Template schema must describe a JSON object");
45
+ }
46
+ const schema = { ...convertedSchema, additionalProperties: convertedSchema.additionalProperties ?? false };
47
+ const transitionMetadata = {
48
+ id: definition.id,
49
+ usesGlobalTransition: definition.usesGlobalTransition ?? false,
50
+ transitionTiming: definition.transitionTiming
51
+ };
52
+ assertTemplateTransitionMetadata(transitionMetadata);
53
+ const { schema: _runtimeSchema, duration, ...definitionWithoutSchema } = definition;
54
+ return Object.freeze({
55
+ label: definition.id,
56
+ description: "",
57
+ usesGlobalTextEffect: false,
58
+ usesGlobalTransition: false,
59
+ usesGlobalBackgroundEffect: false,
60
+ ...definitionWithoutSchema,
61
+ schema,
62
+ ...duration == null ? {} : { preferredDuration: duration }
63
+ });
64
+ }
65
+ function createRenderTemplateRegistry(options) {
66
+ const byId = /* @__PURE__ */ new Map();
67
+ for (const template of options.templates) {
68
+ assertTemplateTransitionMetadata(template);
69
+ if (byId.has(template.id)) throw new Error(`Duplicate template id: ${template.id}`);
70
+ byId.set(template.id, template);
71
+ }
72
+ const templates = Object.freeze([...options.templates]);
73
+ if (options.metadata && options.metadata.map(({ id }) => id).join("\0") !== templates.map(({ id }) => id).join("\0")) {
74
+ throw new Error("Template metadata does not match render templates");
75
+ }
76
+ const metadata = new Map((options.metadata ?? templates.map(toMetadata)).map((template) => [template.id, template]));
77
+ const capabilities = { templates: templates.map(({ id }) => id) };
78
+ return Object.freeze({
79
+ templates,
80
+ capabilities,
81
+ getTemplate: (id) => byId.get(id),
82
+ getTemplateMetadata: (id) => metadata.get(id),
83
+ listTemplateMetadata: () => [...metadata.values()]
84
+ });
85
+ }
86
+ function createTemplateRegistry(options) {
87
+ return createRenderTemplateRegistry({ templates: options.definitions });
88
+ }
6
89
 
7
90
  // src/templates.ts
8
91
  function defineTemplate2(definition) {
package/dist/test.d.ts CHANGED
@@ -1,28 +1,13 @@
1
- import { e as VideoScene, b as VideoOrientation, V as VideoStyle, c as Video, d as VideoAudio, j as VideoInput } from './types-Cg0Onj7l.js';
2
- import { a as VideoWarning, b as VideoFinishReason } from './events-D_1F6gw5.js';
1
+ import { e as VideoScene, b as VideoOrientation, V as VideoStyle, c as Video, d as VideoAudio, j as VideoInput } from './types-BV9IqExh.js';
2
+ import { V as VideoWarning, a as VideoFinishReason } from './events-BBP30j3c.js';
3
3
 
4
4
  type MockVideoStreamPart = {
5
5
  type: "scene.add";
6
6
  placement?: "closer";
7
7
  scene: VideoScene;
8
- } | {
9
- type: "scene.patch";
10
- sceneId: string;
11
- patch: Partial<Omit<VideoScene, "id" | "templateId">>;
12
- } | {
13
- type: "asset.patch";
14
- sceneId: string;
15
- variables: Record<string, unknown>;
16
8
  } | {
17
9
  type: "plan.complete";
18
10
  finishReason?: "stop" | "length" | "content-filter" | "other";
19
- } | {
20
- type: "plan.error";
21
- error: {
22
- message: string;
23
- code?: string;
24
- recoverable?: boolean;
25
- };
26
11
  } | {
27
12
  type: "mock.delay";
28
13
  durationMs: number;
@@ -50,7 +35,7 @@ interface MockProviderTextStream {
50
35
  finishReason: Promise<"stop" | "length" | "content-filter">;
51
36
  }
52
37
  type SimulatedEventEnvelope<TType extends string, TData> = {
53
- protocolVersion: "0.4";
38
+ protocolVersion: "0.5";
54
39
  runId: string;
55
40
  sequence: number;
56
41
  eventId: string;
@@ -73,15 +58,6 @@ type SimulatedVideoEvent = SimulatedEventEnvelope<"response.start", {
73
58
  }> | SimulatedEventEnvelope<"scene.add", {
74
59
  scene: VideoScene;
75
60
  position: number;
76
- revision: number;
77
- }> | SimulatedEventEnvelope<"scene.patch", {
78
- sceneId: string;
79
- revision: number;
80
- patch: Partial<Omit<VideoScene, "id" | "templateId">>;
81
- }> | SimulatedEventEnvelope<"asset.patch", {
82
- sceneId: string;
83
- revision: number;
84
- variables: Record<string, unknown>;
85
61
  }> | SimulatedEventEnvelope<"response.warning", {
86
62
  warning: VideoWarning;
87
63
  }> | SimulatedEventEnvelope<"response.complete", {
package/dist/test.js CHANGED
@@ -233,9 +233,9 @@ async function* simulateVideoStream(parts, options = {}) {
233
233
  if (timeoutMs != null && (!Number.isFinite(timeoutMs) || timeoutMs < 0)) {
234
234
  throw new Error("Simulation timeoutMs must be a non-negative finite number");
235
235
  }
236
- const { createVideo } = await import("./compose-video-6UQ33DOO.js");
237
- const { createTextDeltaVideoPlanner } = await import("./text-stream-UPGUD2TD.js");
238
- const { BUILTIN_SERVER_TEMPLATE_KIT } = await import("./builtin-server-BRTZN4Q7.js");
236
+ const { createVideo } = await import("./compose-video-QJHKTOQ7.js");
237
+ const { createTextDeltaVideoPlanner } = await import("./text-stream-J6EDDJP4.js");
238
+ const { BUILTIN_SERVER_TEMPLATE_KIT } = await import("./builtin-server-YHEZ2JRF.js");
239
239
  const { createTemplateSceneValidator } = await import("./validate-T7GBU2YF.js");
240
240
  const controller = new AbortController();
241
241
  const forwardAbort = () => controller.abort(options.signal?.reason);
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  createTextDeltaVideoPlanner
3
- } from "./chunk-BKF3A357.js";
3
+ } from "./chunk-34O5BY6X.js";
4
4
  import "./chunk-E7CL7UPB.js";
5
- import "./chunk-JW47XCRL.js";
6
- import "./chunk-EE6PK6XC.js";
5
+ import "./chunk-G66Z5CWR.js";
6
+ import "./chunk-AOWSXU2K.js";
7
7
  import "./chunk-XGRA2MUV.js";
8
8
  export {
9
9
  createTextDeltaVideoPlanner
@@ -1,4 +1,4 @@
1
- declare const VIDEO_PROTOCOL_VERSION: "0.4";
1
+ declare const VIDEO_PROTOCOL_VERSION: "0.5";
2
2
  declare const VIDEO_SCHEMA_VERSION: "0.1";
3
3
  type VideoOrientation = "portrait" | "landscape";
4
4
  type VideoKnowledgeMode = "input-only" | "general";
@@ -164,24 +164,9 @@ type VideoPlanPart = {
164
164
  type: "scene.add";
165
165
  placement?: "closer";
166
166
  scene: VideoScene;
167
- } | {
168
- type: "scene.patch";
169
- sceneId: string;
170
- patch: Partial<Omit<VideoScene, "id" | "templateId">>;
171
- } | {
172
- type: "asset.patch";
173
- sceneId: string;
174
- variables: Record<string, unknown>;
175
167
  } | {
176
168
  type: "plan.complete";
177
169
  finishReason?: "stop" | "length" | "content-filter" | "other";
178
- } | {
179
- type: "plan.error";
180
- error: {
181
- message: string;
182
- code?: string;
183
- recoverable?: boolean;
184
- };
185
170
  };
186
171
  type VideoPlanner = (context: VideoGenerationContext) => AsyncIterable<VideoPlanPart>;
187
172
  interface VideoSceneValidationContext {
@@ -11,9 +11,10 @@ npx skills add VanillaSkyAi/video@vanillasky
11
11
 
12
12
  ## Build the first response
13
13
 
14
- 1. Install `@vanillaskyai/video`, `ai`, and one model provider.
15
- 2. Put the provider key in an ignored `.env.local` file.
16
- 3. Connect the application's LLM with `createVideoHandler` in an authenticated
14
+ 1. Install `@vanillaskyai/video`.
15
+ 2. Choose and configure a current model separately; put its provider key in an
16
+ ignored `.env.local` file.
17
+ 3. Connect that app-owned model with `createVideoHandler` in an authenticated
17
18
  server route.
18
19
  4. Pass grounded text or structured data as `input`.
19
20
  5. Call `useVideo()` and render `VideoPlayer` in React.
package/docs/concepts.md CHANGED
@@ -50,12 +50,12 @@ The planner cannot legitimately select a template outside the active kit.
50
50
 
51
51
  ## Planner
52
52
 
53
- The server-side planner turns the input into small plan parts such as
54
- `scene.add`, `scene.patch`, `asset.patch`, and `plan.complete`. A planner may be
53
+ The server-side planner turns the input into complete `scene.add` parts followed
54
+ by `plan.complete`. A planner may be
55
55
  recorded, deterministic, backed by OpenAI or Anthropic, or implemented with
56
56
  another streaming text model.
57
57
 
58
- The planner does not create public event IDs, checksums, revisions, or final
58
+ The planner does not create public event IDs, checksums, or final
59
59
  snapshots. The runtime owns those guarantees.
60
60
 
61
61
  `createVideoHandler` asks the planner to emit one early
@@ -113,5 +113,5 @@ complete storage contract.
113
113
  checksum. Persist the snapshot when you need replay, resume, export, analytics,
114
114
  or an editing handoff. The event log can also be persisted and revalidated.
115
115
 
116
- Read the complete [Protocol 0.4](reference/protocol.md) and the separate
116
+ Read the complete [Protocol 0.5](reference/protocol.md) and the separate
117
117
  [persisted Video 0.1 contract](persistence.md).
@@ -213,9 +213,9 @@ selection, and validator. Import it in the route that connects your model:
213
213
 
214
214
  ```ts
215
215
  // src/video-route.ts
216
- import { openai } from "@ai-sdk/openai";
217
216
  import { streamText } from "ai";
218
217
  import { createVideoHandler } from "@vanillaskyai/video/server";
218
+ import { videoModel } from "./video-model";
219
219
  import { templates } from "../vanillasky/server";
220
220
 
221
221
  export const handleVideo = createVideoHandler({
@@ -226,7 +226,7 @@ export const handleVideo = createVideoHandler({
226
226
  return hostname === "localhost" || hostname === "127.0.0.1";
227
227
  },
228
228
  streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
229
- model: openai(process.env.OPENAI_MODEL ?? "gpt-4.1"),
229
+ model: videoModel,
230
230
  system: systemPrompt,
231
231
  prompt: userPrompt,
232
232
  abortSignal: signal,
@@ -5,26 +5,22 @@
5
5
  Install VanillaSky:
6
6
 
7
7
  ```bash
8
- npm install @vanillaskyai/video@0.4.1 ai @ai-sdk/openai
8
+ npm install @vanillaskyai/video
9
9
  ```
10
10
 
11
- Set your provider key in `.env.local` (never commit it):
11
+ VanillaSky does not select a provider or model during installation. Configure
12
+ that separately on the server. The route below assumes your application exports
13
+ an AI SDK `LanguageModel` as `videoModel`; if it does not, follow
14
+ [Provider integration](provider-integration.md) first.
12
15
 
13
- ```bash
14
- OPENAI_API_KEY=your-key
15
- OPENAI_MODEL=gpt-4.1
16
- ```
17
-
18
- Create one authenticated server route with `createVideoHandler`. Connect your
19
- provider through `streamText`; keep the model, credentials, authentication, rate
16
+ Create one authenticated server route with `createVideoHandler`. Connect the
17
+ app-owned model through `streamText`; keep credentials, authentication, rate
20
18
  limits, and media policy on the server:
21
19
 
22
20
  ```ts
23
- import { openai } from "@ai-sdk/openai";
24
21
  import { streamText } from "ai";
25
22
  import { createVideoHandler } from "@vanillaskyai/video/server";
26
-
27
- const model = openai(process.env.OPENAI_MODEL ?? "gpt-4.1");
23
+ import { videoModel } from "@/lib/video-model";
28
24
 
29
25
  const handle = createVideoHandler({
30
26
  // Local development only. Replace with your session check before deploying.
@@ -34,7 +30,7 @@ const handle = createVideoHandler({
34
30
  return hostname === "localhost" || hostname === "127.0.0.1";
35
31
  },
36
32
  streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
37
- model,
33
+ model: videoModel,
38
34
  system: systemPrompt,
39
35
  prompt: userPrompt,
40
36
  abortSignal: signal,
@@ -49,12 +45,11 @@ The local bypass is intentionally fail-closed: the packaged development command
49
45
  sets its marker only for `next dev`, and it accepts only localhost. Every
50
46
  production request is denied. Replace it with your real session validation
51
47
  before deploying. For literal files and commands,
52
- use the tested
53
- [`examples/nextjs-quickstart` directory](https://github.com/VanillaSkyAi/video/tree/v0.4.1/examples/nextjs-quickstart).
48
+ use the tested [`examples/nextjs-quickstart` directory](../examples/nextjs-quickstart).
54
49
 
55
- `model` can come from any AI SDK provider, registry, gateway, compatible API,
56
- or custom implementation. The application can choose a cheaper or faster model
57
- per request without changing VanillaSky.
50
+ `videoModel` can come from any AI SDK provider, registry, gateway, compatible
51
+ API, or custom implementation. The application can choose a cheaper, faster,
52
+ or higher-quality model per request without changing VanillaSky.
58
53
 
59
54
  Call that route from React and render the player:
60
55
 
@@ -2,27 +2,31 @@
2
2
 
3
3
  # Next.js integration
4
4
 
5
- Install VanillaSky and one AI SDK provider:
5
+ After installing VanillaSky, choose the provider and model for your server. This
6
+ complete example uses Anthropic and asks you to select a current Claude Sonnet
7
+ model explicitly instead of baking a model ID into the SDK docs:
6
8
 
7
9
  ```bash
8
- npm install @vanillaskyai/video@0.4.1 ai @ai-sdk/openai
10
+ npm install @vanillaskyai/video ai @ai-sdk/anthropic
9
11
  ```
10
12
 
11
13
  Create an ignored `.env.local`:
12
14
 
13
15
  ```bash
14
- OPENAI_API_KEY=your-key
15
- OPENAI_MODEL=gpt-4.1
16
+ ANTHROPIC_API_KEY=your-key
17
+ ANTHROPIC_MODEL=your-current-sonnet-model
16
18
  ```
17
19
 
18
20
  Create `app/api/video/route.ts`:
19
21
 
20
22
  ```ts
21
- import { openai } from "@ai-sdk/openai";
23
+ import { anthropic } from "@ai-sdk/anthropic";
22
24
  import { streamText } from "ai";
23
25
  import { createVideoHandler } from "@vanillaskyai/video/server";
24
26
 
25
- const model = openai(process.env.OPENAI_MODEL ?? "gpt-4.1");
27
+ const modelId = process.env.ANTHROPIC_MODEL;
28
+ if (!modelId) throw new Error("Set ANTHROPIC_MODEL in the server environment");
29
+ const model = anthropic(modelId);
26
30
 
27
31
  const handle = createVideoHandler({
28
32
  // Local development only. Replace with your session check before deploying.
@@ -74,9 +78,9 @@ requests; replace it with your application's session validation before
74
78
  deploying.
75
79
 
76
80
  The copy-and-run app is in the
77
- [`examples/nextjs-quickstart` directory](https://github.com/VanillaSkyAi/video/tree/v0.4.1/examples/nextjs-quickstart).
81
+ [`examples/nextjs-quickstart` directory](../examples/nextjs-quickstart).
78
82
 
79
- For another LLM, replace `openai(...)` with the matching AI SDK model. The route
83
+ For another LLM, replace `anthropic(...)` with the matching AI SDK model. The route
80
84
  shape and React code stay the same. See [Provider integration](provider-integration.md)
81
85
  for adapters, authentication, diagnostics, and production controls. Add media,
82
86
  audio, persistence, or custom templates only after the default path works.
@@ -8,7 +8,7 @@ does not provide a database or hosted media store. Save the value returned by
8
8
  no SDK serializer.
9
9
 
10
10
  Every stored video has `schemaVersion: "0.1"`. This storage version is separate
11
- from streaming protocol `0.4`. The 0.1 policy supports the current storage
11
+ from streaming protocol `0.5`. The 0.1 policy supports the current storage
12
12
  schema only: there are no compatibility aliases or implicit coercions.
13
13
 
14
14
  ## Load at the storage boundary
@@ -95,8 +95,8 @@ Your provider streams text deltas. VanillaSky decodes them into typed planner
95
95
  parts, validates each complete scene, and emits deterministic protocol events.
96
96
  The model never returns React, HTML, CSS, or executable JavaScript.
97
97
 
98
- Generated `scene.add`, `scene.patch`, and `asset.patch` operations all pass
99
- through the same merged-scene validation before the player sees them.
98
+ Every generated `scene.add` is complete and passes validation before the player
99
+ sees it. Emitted scenes are immutable.
100
100
 
101
101
  The standard planner contract requires one `scene.add` with
102
102
  `placement: "closer"` immediately after the first playable body scene. The