@vanillaskyai/video 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/PUBLIC-API.md +123 -9
  3. package/README.md +69 -115
  4. package/dist/{bg-confetti-SHQI7ATB.js → bg-confetti-HITCGLGD.js} +1 -1
  5. package/dist/{bg-emoji-XYFRA63Q.js → bg-emoji-VU4UZYZN.js} +1 -1
  6. package/dist/{bg-media-G4XOQEAS.js → bg-media-L34PDQXJ.js} +2 -2
  7. package/dist/{brand-message-XHONEDNW.js → brand-message-FUJ4STFO.js} +1 -1
  8. package/dist/{builtin-server-YHEZ2JRF.js → builtin-server-KB7FKF6A.js} +2 -2
  9. package/dist/{chart-bar-HRHD57ML.js → chart-bar-EGPHJATL.js} +2 -2
  10. package/dist/{chart-counter-KFPV44RB.js → chart-counter-5FFWQNZO.js} +2 -2
  11. package/dist/{chart-progress-ring-OR2VOIHV.js → chart-progress-ring-JMXQLMLC.js} +2 -2
  12. package/dist/{chunk-66MNRUCR.js → chunk-2PYO6VAC.js} +51 -3
  13. package/dist/{chunk-ZQQQAAKP.js → chunk-2XT4MZ76.js} +33 -2
  14. package/dist/{chunk-PNS52FL4.js → chunk-5SLENAJW.js} +19 -6
  15. package/dist/{chunk-EFL34TXF.js → chunk-AITKH6QT.js} +24 -8
  16. package/dist/{chunk-FZHMQFG3.js → chunk-GAIOHGNR.js} +5 -21
  17. package/dist/{chunk-3RV4YKB3.js → chunk-JNN3EYVP.js} +51 -26
  18. package/dist/{chunk-HVCPEAQF.js → chunk-LKBZX7GV.js} +9 -6
  19. package/dist/{chunk-G66Z5CWR.js → chunk-MMUXVA47.js} +27 -3
  20. package/dist/{chunk-7BGAU6C3.js → chunk-MXZSDGZQ.js} +2 -1
  21. package/dist/{chunk-3OU7HIEB.js → chunk-RE4IMWJR.js} +15 -31
  22. package/dist/{chunk-FVTMYS6U.js → chunk-RGF452LL.js} +1 -1
  23. package/dist/{chunk-34O5BY6X.js → chunk-RXHW4EP4.js} +21 -4
  24. package/dist/chunk-SKRGRKHY.js +142 -0
  25. package/dist/{chunk-TCEHK2BW.js → chunk-YJJC4N4D.js} +2 -1
  26. package/dist/cli.js +438 -30
  27. package/dist/{compose-video-JWIEDTEJ.js → compose-video-PD6LKKRK.js} +4 -4
  28. package/dist/{cta-logo-JJOGK4UC.js → cta-logo-YR7LYUR5.js} +1 -1
  29. package/dist/{cta-media-IQ3MAWEH.js → cta-media-DQJBKUO2.js} +2 -2
  30. package/dist/{events-BBP30j3c.d.ts → events-B6qS1Lsb.d.ts} +2 -2
  31. package/dist/{incoming-call-KXF67OT2.js → incoming-call-54KQ7O5A.js} +1 -1
  32. package/dist/index.d.ts +89 -3
  33. package/dist/index.js +12 -1
  34. package/dist/{infographic-before-after-2I7YZVDD.js → infographic-before-after-ROS52GOW.js} +1 -1
  35. package/dist/{infographic-feature-list-QDM4I7XE.js → infographic-feature-list-E7MGUDAN.js} +2 -2
  36. package/dist/{infographic-problem-solution-FMLJCPG6.js → infographic-problem-solution-3S6NGO5N.js} +2 -2
  37. package/dist/{infographic-stat-row-VFTIXS5F.js → infographic-stat-row-SYB7AB7H.js} +2 -2
  38. package/dist/{infographic-steps-L7W24Z35.js → infographic-steps-KCE6JWE2.js} +2 -2
  39. package/dist/{kit-DEG3fcaL.d.ts → kit-8g46H2RZ.d.ts} +1 -1
  40. package/dist/{prompt-input-F3CYU2XH.js → prompt-input-MHX4O42G.js} +1 -1
  41. package/dist/react.d.ts +194 -6
  42. package/dist/react.js +1849 -40
  43. package/dist/{reaction-2XCLOJOK.js → reaction-SLEGR3BE.js} +2 -2
  44. package/dist/server.d.ts +102 -4
  45. package/dist/server.js +972 -27
  46. package/dist/{showcase-code-KHSMIPG6.js → showcase-code-2HLAVKBM.js} +2 -2
  47. package/dist/{showcase-phone-M4CEANIW.js → showcase-phone-Q4HEO4LH.js} +2 -2
  48. package/dist/{showcase-terminal-26I3DYF7.js → showcase-terminal-DL45LUFG.js} +2 -2
  49. package/dist/{showcase-web-TY6CZV2Z.js → showcase-web-2QZO73FZ.js} +2 -2
  50. package/dist/{social-milestone-TF6MX7ET.js → social-milestone-GEFUQSBF.js} +1 -1
  51. package/dist/{social-notification-2RJDAZSF.js → social-notification-FJ5VHVLK.js} +1 -1
  52. package/dist/{social-review-stack-2ICKJQG7.js → social-review-stack-KXQCKFRP.js} +1 -1
  53. package/dist/{social-testimonial-X7OGADA3.js → social-testimonial-JZ7CHC7S.js} +1 -1
  54. package/dist/{social-tweet-TK7SH2F6.js → social-tweet-UCDXM25F.js} +1 -1
  55. package/dist/{system-prompt-4I6Z5HK3.js → system-prompt-RRXIWDDD.js} +5 -3
  56. package/dist/template-catalog.js +1 -1
  57. package/dist/templates.d.ts +3 -3
  58. package/dist/test.d.ts +2 -2
  59. package/dist/test.js +3 -3
  60. package/dist/{text-stream-J6EDDJP4.js → text-stream-FW4BLBAL.js} +2 -2
  61. package/dist/{types-BV9IqExh.d.ts → types-2wHBqtg8.d.ts} +29 -1
  62. package/dist/types-DrABlRa7.d.ts +46 -0
  63. package/docs/agent-integration.md +49 -25
  64. package/docs/architecture.md +37 -22
  65. package/docs/branding-and-personalization.md +1 -1
  66. package/docs/concepts.md +14 -0
  67. package/docs/custom-templates.md +9 -9
  68. package/docs/customization.md +41 -5
  69. package/docs/getting-started.md +81 -84
  70. package/docs/media-and-audio.md +103 -166
  71. package/docs/persistence.md +1 -1
  72. package/docs/production.md +81 -92
  73. package/docs/prompt-and-input.md +71 -161
  74. package/docs/provider-integration.md +91 -49
  75. package/docs/responsive-orientation.md +1 -1
  76. package/docs/security.md +6 -5
  77. package/docs/streaming-protocol.md +1 -1
  78. package/docs/testing.md +51 -43
  79. package/examples/custom-template/README.md +1 -1
  80. package/package.json +20 -22
  81. package/registry/items/barChart.json +6 -4
  82. package/registry/items/beforeAfter.json +1 -1
  83. package/registry/items/bigNumber.json +4 -3
  84. package/registry/items/brandMessage.json +3 -2
  85. package/registry/items/cardList.json +6 -4
  86. package/registry/items/codeEditor.json +4 -3
  87. package/registry/items/confetti.json +1 -1
  88. package/registry/items/ctaLogo.json +3 -2
  89. package/registry/items/ctaMedia.json +4 -3
  90. package/registry/items/emojiBurst.json +1 -1
  91. package/registry/items/incomingCall.json +3 -2
  92. package/registry/items/media.json +4 -3
  93. package/registry/items/milestone.json +3 -2
  94. package/registry/items/notification.json +3 -2
  95. package/registry/items/phoneMockup.json +4 -3
  96. package/registry/items/problemSolution.json +4 -3
  97. package/registry/items/progressRing.json +4 -3
  98. package/registry/items/promptInput.json +3 -2
  99. package/registry/items/reaction.json +2 -2
  100. package/registry/items/reviewStack.json +3 -2
  101. package/registry/items/steps.json +6 -4
  102. package/registry/items/terminal.json +5 -4
  103. package/registry/items/testimonial.json +3 -2
  104. package/registry/items/tripleStats.json +4 -3
  105. package/registry/items/tweet.json +3 -2
  106. package/registry/items/webMockup.json +4 -3
  107. package/starters/video-chat/.env.example +16 -0
  108. package/starters/video-chat/README.md +63 -0
  109. package/starters/video-chat/index.html +12 -0
  110. package/starters/video-chat/package.json +28 -0
  111. package/starters/video-chat/server.ts +152 -0
  112. package/starters/video-chat/src/main.tsx +8 -0
  113. package/starters/video-chat/stock.ts +139 -0
  114. package/starters/video-chat/tsconfig.json +20 -0
  115. package/starters/video-chat/vite.config.ts +81 -0
  116. package/styles/video-chat.css +713 -0
  117. package/docs/input-and-first-scene.md +0 -64
  118. package/docs/integrate-nextjs.md +0 -86
  119. package/docs/live-channels.md +0 -149
  120. package/docs/use-cases.md +0 -59
  121. package/examples/nextjs-quickstart/.env.example +0 -2
  122. package/examples/nextjs-quickstart/README.md +0 -27
  123. package/examples/nextjs-quickstart/next-env.d.ts +0 -4
  124. package/examples/nextjs-quickstart/package.json +0 -25
  125. package/examples/nextjs-quickstart/src/app/api/video/route.ts +0 -33
  126. package/examples/nextjs-quickstart/src/app/layout.tsx +0 -5
  127. package/examples/nextjs-quickstart/src/app/page.tsx +0 -31
  128. package/examples/nextjs-quickstart/tsconfig.json +0 -26
  129. package/examples/server-integrations/README.md +0 -20
  130. package/examples/server-integrations/src/ai-sdk-media.ts +0 -90
@@ -1,49 +1,96 @@
1
- [← Documentation home](../README.md) · [Previous: Next.js](integrate-nextjs.md) · [Next: Input and first scene →](input-and-first-scene.md)
1
+ [← Documentation home](../README.md) · [Previous: Getting started](getting-started.md) · [Next: Branding and personalization →](branding-and-personalization.md)
2
2
 
3
3
  # Provider integration
4
4
 
5
- The model connection enters the SDK through `streamText` in
6
- `src/server/create-video-handler.ts`. The SDK does not instantiate or choose a
7
- model; the application passes the generated `systemPrompt` and `userPrompt` to
8
- its provider here.
5
+ Start from the generated chat so provider work stays confined to the
6
+ application-owned server:
9
7
 
10
- Use the AI SDK as the normal app-owned provider adapter. Any AI SDK
11
- `LanguageModel` works. Keep selection in one server-only module so OpenAI and
12
- Anthropic use the same route and React component:
8
+ ```bash
9
+ npx @vanillaskyai/video init
10
+ npx vanillasky doctor
11
+ npm run dev
12
+ ```
13
+
14
+ The generated `server.ts` is the canonical provider example. It starts with one
15
+ text key, packaged templates, and browser voice. Adding the optional generated
16
+ speech, stock, transcription, or generated-video key enables its callback and
17
+ advertises the capability automatically; `src/main.tsx` does not change.
18
+
19
+ For the full chat experience, mount one `createVideoChatHandler` and keep every
20
+ provider choice in its callbacks:
13
21
 
14
22
  ```ts
15
23
  import "server-only";
16
24
  import { anthropic } from "@ai-sdk/anthropic";
17
- import { openai } from "@ai-sdk/openai";
18
- import { streamText } from "ai";
19
- import { createVideoHandler } from "@vanillaskyai/video/server";
20
-
21
- const provider = process.env.VIDEO_PROVIDER;
22
- const modelId = process.env.VIDEO_MODEL;
23
- if (!modelId) throw new Error("Set VIDEO_MODEL in the server environment");
24
- const model = provider === "openai" ? openai(modelId)
25
- : provider === "anthropic" ? anthropic(modelId)
26
- : (() => { throw new Error("Set VIDEO_PROVIDER to openai or anthropic"); })();
27
-
28
- export const POST = createVideoHandler({
25
+ import { generateText, streamText } from "ai";
26
+ import { createVideoChatHandler } from "@vanillaskyai/video/server";
27
+
28
+ export const handler = createVideoChatHandler({
29
29
  authorize: verifySession,
30
30
  streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
31
- model,
31
+ model: anthropic(process.env.TEXT_MODEL ?? "claude-sonnet-5"),
32
32
  system: systemPrompt,
33
33
  prompt: userPrompt,
34
34
  abortSignal: signal,
35
35
  }),
36
+ generateText: async ({ systemPrompt, userPrompt, maxOutputTokens, signal }) => {
37
+ const result = await generateText({
38
+ model: anthropic(process.env.TEXT_MODEL ?? "claude-sonnet-5"),
39
+ system: systemPrompt,
40
+ prompt: userPrompt,
41
+ maxOutputTokens,
42
+ abortSignal: signal,
43
+ });
44
+ return result.text;
45
+ },
36
46
  });
37
47
  ```
38
48
 
39
- The AI SDK result can be returned directly: its `textStream`, `finishReason`,
40
- `rawFinishReason`, usage, provider metadata, warnings, and response metadata
41
- match VanillaSky's structural callback contract. Keep the
42
- provider, model, and credentials on the server. The callback runs per request,
43
- so an application can route cheap/fast and high-capability models through the
44
- same handler without adding a VanillaSky abstraction. See
45
- [provider adapter reference](reference/provider-adapters.md) for model alternatives and advanced native
46
- provider loops.
49
+ That one text provider gives the browser templated video responses and local
50
+ browser speech. Supplying `generateSpeech`, `transcribe`, `searchMedia`, or
51
+ `generateVideo` enables those capabilities automatically. The callbacks are
52
+ structural and provider-neutral; their SDKs and credentials remain application
53
+ dependencies and never enter the browser bundle.
54
+
55
+ The planner emits a 6-9 word hook first, then continues into the scenes in the
56
+ same stream. The stock lookup is a separate cancellable request, so it cannot
57
+ delay speech or planning. A welcome card can carry a prewritten `opening`, which
58
+ starts immediately with its already-loaded media. For a full AI response, the
59
+ same first streamed object reserves the exact first body scene. Its clip starts
60
+ generating while the planner continues with scenes two through five. Template
61
+ responses stream their first resolved scene without waiting for the rest of the
62
+ plan, and normal narration is written in that same planner call rather than
63
+ through a second model round trip.
64
+
65
+ The matching complete React interface is one component and one scoped style
66
+ import:
67
+
68
+ ```tsx
69
+ import { VideoChat } from "@vanillaskyai/video/react";
70
+ import "@vanillaskyai/video/video-chat.css";
71
+
72
+ export function App() {
73
+ return <VideoChat />;
74
+ }
75
+ ```
76
+
77
+ ## Custom interface
78
+
79
+ For a custom interface, use `useVideoChat` and render its `turns`, `welcome`,
80
+ `suggestions`, `caption`, and `status`; the hook owns their network and playback
81
+ lifecycle. Pass a selected card through
82
+ `chat.ask(card.prompt, { opening: card.opening, openingMedia: card.media })` to
83
+ start its hook and reuse its footage immediately. For typed prompts, the hook
84
+ and media keyword arrive through the response stream automatically.
85
+
86
+ Any AI SDK `LanguageModel` works in both `streamText` and `generateText`. Keep
87
+ selection in one server-only module when an application supports several text
88
+ providers. The chat route and React component stay unchanged; only the model
89
+ passed to those callbacks changes. The AI SDK result can be returned directly:
90
+ its text stream, finish reason, usage, warnings, and response metadata match the
91
+ structural callback contract. See the
92
+ [provider adapter reference](reference/provider-adapters.md) for native provider
93
+ alternatives.
47
94
 
48
95
  ## Planning effort and reasoning modes
49
96
 
@@ -85,7 +132,7 @@ and credentials stay with the application.
85
132
  Use `onComplete` for server-side cost and completion measurement:
86
133
 
87
134
  ```ts
88
- createVideoHandler({
135
+ createVideoChatHandler({
89
136
  authorize: verifySession,
90
137
  streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
91
138
  model,
@@ -93,6 +140,7 @@ createVideoHandler({
93
140
  prompt: userPrompt,
94
141
  abortSignal: signal,
95
142
  }),
143
+ generateText: runSmallTextTask,
96
144
  onWarning: (warning) => logSafeWarning(warning.code, warning.category),
97
145
  onComplete: (summary) => recordGeneration({
98
146
  finishReason: summary.finishReason,
@@ -113,23 +161,18 @@ true` only when the host deliberately needs bounded provider-native usage and
113
161
  metadata and has an appropriate retention policy.
114
162
 
115
163
  `acceptedSceneCount`, `rejectedSceneCount`, and `timeToFirstSceneMs` describe
116
- model-generated scene additions; a deterministic supplied opening is not
117
- counted. Their sum is the proposed scene count. `videoDurationSec` is the
118
- duration actually committed; compare it with the `maxDurationSec` supplied by
119
- your application when applying a retry policy. These fields provide a
120
- server-side quality signal without exposing model metadata in the browser.
164
+ model-generated scene additions; the streamed opening hook is not counted.
165
+ Their sum is the proposed scene count. `videoDurationSec` is the duration
166
+ actually committed. These fields provide a server-side quality signal without
167
+ exposing model metadata in the browser.
121
168
  Warnings include the same bounded typed warnings emitted to the client.
122
169
  `plan_incomplete` identifies a playable partial response whose planner reported
123
170
  a length limit; applications should show that result as incomplete and may
124
171
  offer a bounded retry with a larger output or duration budget.
125
- `plan_missing_closer` identifies a playable plan that ended without the
126
- explicit final scene required by the standard handler. `createVideoHandler`
127
- sets `requireCloser: true` by default. Specialized deterministic integrations
128
- may set `requireCloser: false`; ordinary AI planners should keep the default.
129
- For non-interactive backfills, define an application threshold (for example,
130
- no rejected scenes and a useful committed-duration ratio) and retry a bounded
131
- number of times. Keep the best accepted result rather than treating
132
- `finishReason: "stop"` alone as a quality score.
172
+ `plan_missing_closer` identifies a playable answer that ended without its
173
+ explicit final scene. For non-interactive evaluation, define an application
174
+ threshold and retry a bounded number of times. Keep the best accepted result
175
+ rather than treating `finishReason: "stop"` alone as a quality score.
133
176
 
134
177
  The generated system prompt includes the selected trusted-template catalog and
135
178
  is intentionally substantial. It is stable for the same SDK version, template
@@ -150,9 +193,8 @@ same explicit request budget.
150
193
 
151
194
  ## Product-level planner guidance
152
195
 
153
- `createVideoHandler` constructs the planner prompt from the generated server
154
- template registry. Normal integrations do not build prompts or capabilities.
155
- Use the handler's `basePrompt` option only for durable product-level direction.
156
- `VideoInput.knowledgeMode` owns the knowledge boundary: `input-only` is strict
157
- and remains the default, while `general` permits stable model knowledge.
158
- Presentation guidance in `basePrompt` or `instructions` never changes it.
196
+ `createVideoChatHandler` constructs the planner prompt from the trusted template
197
+ registry. Normal integrations do not build prompts or capabilities. Use the
198
+ handler's `instructions` option for durable product-level direction such as a
199
+ character, audience, domain, or answer style. The current user prompt and
200
+ bounded prior turns are supplied separately by the SDK.
@@ -1,4 +1,4 @@
1
- [← Documentation home](../README.md) · [Previous: Input and first scene](input-and-first-scene.md)
1
+ [← Documentation home](../README.md) · [Previous: Prompt and conversation input](prompt-and-input.md)
2
2
 
3
3
  # Responsive orientation
4
4
 
package/docs/security.md CHANGED
@@ -7,7 +7,8 @@ The SDK validates protocol shape; your application still owns identity, authoriz
7
7
  ## Required server controls
8
8
 
9
9
  - Authenticate the user and tenant before reading the prompt body.
10
- - `createVideoHandler` requires an explicit `authorize` policy. The
10
+ - `createVideoChatHandler` and the lower-level `createVideoHandler` require an
11
+ explicit `authorize` policy. The
11
12
  `authorize: "none"` escape hatch is for intentionally non-public in-process
12
13
  tests only; do not use it on a billable generation route.
13
14
  - Allowlist browser origins; CORS is not authentication.
@@ -15,10 +16,10 @@ The SDK validates protocol shape; your application still owns identity, authoriz
15
16
  - Keep provider keys, system prompts, tools, signed-URL credentials, and admin tokens server-side.
16
17
  - Use the same generated template registry on the server and in React; the handler infers validation.
17
18
  - Treat every project-owned template as trusted application build code and
18
- review it before using the CLI. Normal `vanillasky list`,
19
- `vanillasky describe`, `vanillasky add`, `vanillasky sync`, and
20
- `vanillasky check` commands execute project template modules locally. This
21
- includes `vanillasky add` previews with `--dry-run` or `--diff`, because the
19
+ review it before using the CLI. Normal `vanillasky templates list`,
20
+ `vanillasky templates describe`, `vanillasky templates add`, `vanillasky templates sync`, and
21
+ `vanillasky templates check` commands execute project template modules locally. This
22
+ includes `vanillasky templates add` previews with `--dry-run` or `--diff`, because the
22
23
  CLI must derive the proposed browser and server registries. Resource and
23
24
  environment boundaries reduce accidental damage but are not a portable
24
25
  JavaScript sandbox.
@@ -1,4 +1,4 @@
1
- [← Documentation home](../README.md) · [Previous: Live channels](live-channels.md) · [Next: Security →](security.md)
1
+ [← Documentation home](../README.md) · [Previous: Persistence and replay](persistence.md) · [Next: Security →](security.md)
2
2
 
3
3
  # Streaming protocol
4
4
 
package/docs/testing.md CHANGED
@@ -1,56 +1,63 @@
1
- # Test integrations without a model
1
+ # Test video chat without a model
2
2
 
3
- `@vanillaskyai/video/test` provides deterministic provider streams and in-process
4
- protocol events. It has no React or provider-SDK dependency, makes no network
5
- request, and does not require a model key.
3
+ `@vanillaskyai/video/test` provides deterministic planner streams and
4
+ in-process protocol events. It has no React or provider-SDK dependency, makes
5
+ no network request, and needs no model key.
6
6
 
7
- ## Test a route handler with Vitest
7
+ ## Test the chat route with Vitest
8
8
 
9
- Pass `createMockVideoPlanner()` directly to the same `createVideoHandler()`
10
- used by the application. Call the handler with a standard `Request`; no live
11
- HTTP server is involved.
9
+ Pass `createMockVideoPlanner()` to the same `createVideoChatHandler` used by the
10
+ application. A standard `Request` exercises parsing, validation, pacing, the
11
+ opening extension, and SSE without starting an HTTP server.
12
12
 
13
13
  ```ts
14
14
  import { describe, expect, it } from "vitest";
15
- import { createVideoHandler } from "@vanillaskyai/video/server";
16
- import {
17
- createMockVideoPlanner,
18
- videoFixtures,
19
- } from "@vanillaskyai/video/test";
15
+ import { createVideoChatHandler } from "@vanillaskyai/video/server";
16
+ import { createMockVideoPlanner } from "@vanillaskyai/video/test";
20
17
 
21
- describe("POST /api/video", () => {
22
- it("returns a completed video stream", async () => {
23
- const handle = createVideoHandler({
24
- authorize: "none", // Explicit because this in-process test route is never public.
18
+ describe("POST /api/video-chat", () => {
19
+ it("returns a completed video answer", async () => {
20
+ const handle = createVideoChatHandler({
21
+ authorize: "none", // Only acceptable because this handler stays in process.
25
22
  heartbeatMs: false,
26
23
  streamText: createMockVideoPlanner(),
24
+ generateText: async ({ task }) => task === "suggestions"
25
+ ? JSON.stringify({ suggestions: [] })
26
+ : "A deterministic narration line.",
27
27
  });
28
- const response = await handle(new Request("https://app.test/api/video", {
29
- method: "POST",
30
- body: JSON.stringify({
31
- protocolVersion: "0.5",
32
- requestId: "route-test",
33
- input: videoFixtures.portrait.input,
34
- }),
35
- }));
28
+
29
+ const response = await handle(new Request(
30
+ "https://app.test/api/video-chat?action=response",
31
+ {
32
+ method: "POST",
33
+ headers: { "content-type": "application/json" },
34
+ body: JSON.stringify({
35
+ prompt: "Explain why the Moon shows one face",
36
+ opening: "The Moon turns, perfectly matching its orbit.",
37
+ mode: "templates",
38
+ orientation: "landscape",
39
+ }),
40
+ },
41
+ ));
36
42
  const body = await response.text();
37
43
 
38
44
  expect(response.status).toBe(200);
45
+ expect(body).toContain('"type":"data.video-chat-opening"');
39
46
  expect(body).toContain('"type":"response.complete"');
40
47
  });
41
48
  });
42
49
  ```
43
50
 
44
- The mock emits provider-style newline-delimited JSON. The route still parses,
45
- validates, paces, and converts those plan parts into SSE exactly as it does with
46
- a live provider.
51
+ Also request `?action=capabilities` and `?action=welcome` in route tests. Assert
52
+ that optional modes and controls appear only when their callbacks are present.
53
+ For browser coverage, render `<VideoChat />`, submit through the visible prompt,
54
+ and wait for a known template rather than reaching into hook internals.
47
55
 
48
56
  ## Test protocol events in process
49
57
 
50
- `simulateVideoStream(parts, options?)` runs the same composition and validation
51
- pipeline without an HTTP boundary. It yields structural, discriminated event
52
- objects. Default IDs are `test-request` and `test-run`; override `requestId` and
53
- `runId` when a test needs different fixed values.
58
+ `simulateVideoStream(parts, options?)` runs the composition and validation
59
+ pipeline without an HTTP boundary. It yields typed event objects and remains
60
+ useful for focused planner and protocol cases beneath the chat handler.
54
61
 
55
62
  ```ts
56
63
  import { expect, it } from "vitest";
@@ -74,9 +81,8 @@ it("keeps a truncated result playable", async () => {
74
81
  });
75
82
  ```
76
83
 
77
- The portrait and landscape fixtures each contain `{ input, parts }`. All public
78
- fixture values are deeply frozen, and each helper clones parts before a run so
79
- one test cannot mutate another test's input.
84
+ The portrait and landscape fixtures each contain `{ input, parts }`. Public
85
+ fixture values are deeply frozen and every helper clones parts before a run.
80
86
 
81
87
  ## Delays, aborts, and timeouts
82
88
 
@@ -113,11 +119,11 @@ it("times out deterministically", async () => {
113
119
  });
114
120
  ```
115
121
 
116
- For a host cancellation, pass an `AbortController` signal and abort it after the
117
- desired partial event. Timeouts are opt-in and local to the simulator; at the
118
- route boundary the host remains responsible for aborting the request signal.
122
+ For host cancellation, pass an `AbortController` signal and abort after the
123
+ desired partial event. At the route boundary, the host remains responsible for
124
+ aborting the request signal and each provider callback must honour it.
119
125
 
120
- ## Fixed scenarios
126
+ ## Fixed planner scenarios
121
127
 
122
128
  `createMockVideoPlanner({ scenario })` accepts:
123
129
 
@@ -126,12 +132,14 @@ route boundary the host remains responsible for aborting the request signal.
126
132
  | `success` | Completed portrait fixture |
127
133
  | `delayed` | Success after a 25 ms fake-timer-safe delay |
128
134
  | `truncated` | Partial playable result completed with `length` |
129
- | `invalidScene` | Invalid scene dropped with a recoverable diagnostic, then completion |
135
+ | `invalidScene` | Invalid scene dropped with a recoverable warning, then completion |
130
136
  | `providerFailure` | Redacted terminal generation failure |
131
137
  | `contentFilter` | Partial playable result completed with `content-filter` |
132
138
  | `abort` | Waits for the supplied request signal to abort |
133
139
  | `timeout` | Waits for the host signal or simulator `timeoutMs` |
134
140
 
135
- Use `parts` in `MockVideoPlannerOptions` to supply custom structural plan parts,
136
- and `delayMs` to delay every emitted provider chunk. Provider selection,
137
- credentials, retries, and real-provider acceptance remain application-owned.
141
+ Use `parts` for custom structural plan parts and `delayMs` to delay each
142
+ provider chunk. Keep provider selection, credentials, retries, and real-model
143
+ acceptance outside deterministic CI.
144
+
145
+ [← Documentation home](../README.md)
@@ -2,7 +2,7 @@
2
2
 
3
3
  These files are deliberately complete, one-file examples. Copy the closest
4
4
  shape into `vanillasky/templates/`, rename its ID, then run `npx vanillasky
5
- sync` and `npx vanillasky check`.
5
+ templates sync` and `npx vanillasky templates check`.
6
6
 
7
7
  | Reference | Use it when |
8
8
  | --- | --- |
package/package.json CHANGED
@@ -1,20 +1,23 @@
1
1
  {
2
2
  "name": "@vanillaskyai/video",
3
- "version": "0.6.0",
4
- "description": "Open-source video response SDK for personalized AI applications.",
3
+ "version": "0.7.0",
4
+ "description": "Open-source voice-and-video chat SDK for AI applications.",
5
5
  "keywords": [
6
+ "video-chat",
7
+ "voice-chat",
8
+ "ai-chat",
6
9
  "generative-video",
7
- "personalized-video",
8
10
  "video-response",
9
11
  "ai-output",
10
- "contextual-ai",
11
12
  "streaming",
12
13
  "react",
13
14
  "llm",
14
15
  "video"
15
16
  ],
16
17
  "type": "module",
17
- "sideEffects": false,
18
+ "sideEffects": [
19
+ "./styles/video-chat.css"
20
+ ],
18
21
  "license": "Apache-2.0",
19
22
  "repository": {
20
23
  "type": "git",
@@ -27,15 +30,18 @@
27
30
  "files": [
28
31
  "bin",
29
32
  "dist",
33
+ "styles/video-chat.css",
34
+ "starters/video-chat/.env.example",
35
+ "starters/video-chat/README.md",
36
+ "starters/video-chat/index.html",
37
+ "starters/video-chat/package.json",
38
+ "starters/video-chat/server.ts",
39
+ "starters/video-chat/src/main.tsx",
40
+ "starters/video-chat/stock.ts",
41
+ "starters/video-chat/tsconfig.json",
42
+ "starters/video-chat/vite.config.ts",
30
43
  "registry/items",
31
44
  "examples/custom-template",
32
- "examples/nextjs-quickstart/.env.example",
33
- "examples/nextjs-quickstart/README.md",
34
- "examples/nextjs-quickstart/next-env.d.ts",
35
- "examples/nextjs-quickstart/package.json",
36
- "examples/nextjs-quickstart/src",
37
- "examples/nextjs-quickstart/tsconfig.json",
38
- "examples/server-integrations/src/ai-sdk-media.ts",
39
45
  "README.md",
40
46
  "LICENSE",
41
47
  "SECURITY.md",
@@ -80,7 +86,8 @@
80
86
  "types": "./dist/test.d.ts",
81
87
  "import": "./dist/test.js",
82
88
  "default": "./dist/test.js"
83
- }
89
+ },
90
+ "./video-chat.css": "./styles/video-chat.css"
84
91
  },
85
92
  "scripts": {
86
93
  "acceptance:live": "tsx scripts/acceptance/run-live.ts",
@@ -89,14 +96,6 @@
89
96
  "browser:install": "playwright install chromium firefox webkit",
90
97
  "browser:test": "playwright test",
91
98
  "build": "tsup",
92
- "example:install": "npm run examples:install-current",
93
- "example:build": "npm run build --prefix examples/react-vite",
94
- "example:dev": "npm run dev --prefix examples/react-vite",
95
- "example:preview": "npm run preview --prefix examples/react-vite -- --host 127.0.0.1 --port 4187 --strictPort",
96
- "examples:verify-documented": "node scripts/verify-documented-examples.mjs",
97
- "examples:install-current": "npm run build && node scripts/install-current-examples.mjs",
98
- "server-examples:typecheck": "npm run typecheck --prefix examples/server-integrations",
99
- "server-examples:compat": "npm run ai-sdk:compat --prefix examples/server-integrations",
100
99
  "lint": "eslint .",
101
100
  "catalog:sync": "tsx scripts/generate-builtin-catalog.ts",
102
101
  "catalog:check": "tsx scripts/generate-builtin-catalog.ts --check",
@@ -143,7 +142,6 @@
143
142
  "eslint": "^9.39.1",
144
143
  "globals": "^16.5.0",
145
144
  "jsdom": "^26.1.0",
146
- "mdast-util-from-markdown": "2.0.3",
147
145
  "openai": "^7.5.0",
148
146
  "react": "^19.2.8",
149
147
  "react-dom": "^19.2.8",