@vanillaskyai/video 0.8.8 → 0.9.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 (175) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/PUBLIC-API.md +5 -8
  3. package/dist/{builtin-server-CUYPIDE7.js → builtin-server-HW6HGXCZ.js} +2 -2
  4. package/dist/{catalog-types-BIhSpOWK.d.ts → catalog-types-WTbLP6Jh.d.ts} +5 -1
  5. package/dist/chapter-title-YRLIQY3V.js +7 -0
  6. package/dist/check-runtime.js +6 -15
  7. package/dist/{chunk-2USVB3CY.js → chunk-3ZTEFJIR.js} +285 -178
  8. package/dist/chunk-4YM2M62S.js +13 -0
  9. package/dist/chunk-77VY4O7A.js +390 -0
  10. package/dist/{chunk-MMUXVA47.js → chunk-7M56IUUX.js} +9 -8
  11. package/dist/chunk-7NZOMTAL.js +63 -0
  12. package/dist/{chunk-2DOLF4QN.js → chunk-AUPC6MDK.js} +12 -14
  13. package/dist/{chunk-352ZZCET.js → chunk-EGVQODKU.js} +2 -2
  14. package/dist/chunk-EWWCQFTI.js +18 -0
  15. package/dist/{chunk-MXZSDGZQ.js → chunk-G7IZBBWF.js} +14 -9
  16. package/dist/{chunk-KT3M7KDL.js → chunk-HKUI5BTB.js} +3 -3
  17. package/dist/{chunk-LMG3GHLL.js → chunk-IQMYK5DX.js} +29 -3
  18. package/dist/chunk-LH4BPALA.js +564 -0
  19. package/dist/{chunk-OWBT45EN.js → chunk-MQILEEXC.js} +1 -1
  20. package/dist/chunk-MXEYPK7M.js +552 -0
  21. package/dist/{chunk-AITKH6QT.js → chunk-OR52UMSF.js} +13 -39
  22. package/dist/{chunk-5SLENAJW.js → chunk-PAJ44TFW.js} +1 -2
  23. package/dist/{chunk-L7QGGKJ2.js → chunk-PDQFQIQW.js} +1 -1
  24. package/dist/{chunk-WJ6FLKNC.js → chunk-R3XAOMKP.js} +14 -0
  25. package/dist/{chunk-2OKB27TG.js → chunk-RXTN2CW6.js} +2 -2
  26. package/dist/{chunk-XWWLKRNU.js → chunk-TVIU23OM.js} +38 -49
  27. package/dist/{chunk-224QNWRA.js → chunk-UDIODEDK.js} +1 -5
  28. package/dist/{chunk-3VRMKFM7.js → chunk-ZDJNFA52.js} +11 -24
  29. package/dist/cinema-media-YQHSIFDA.js +11 -0
  30. package/dist/cli.js +18 -9
  31. package/dist/comparison-RZMMHZR2.js +27 -0
  32. package/dist/compose-video-TBFTONUC.js +12 -0
  33. package/dist/editorial-timeline-OZX7OS6G.js +36 -0
  34. package/dist/{events-BQ6z264n.d.ts → events-B4YCc4vc.d.ts} +1 -1
  35. package/dist/focus-cards-TJ72BT7U.js +20 -0
  36. package/dist/index.d.ts +4 -6
  37. package/dist/index.js +3 -7
  38. package/dist/key-figure-GYENXKH3.js +20 -0
  39. package/dist/{kit-tQnCPMOM.d.ts → kit-DrRpdn0p.d.ts} +2 -2
  40. package/dist/mobile-message-UZUPIKHD.js +49 -0
  41. package/dist/{preload-media-LJXWKTWG.js → preload-media-CFTZIMZL.js} +1 -1
  42. package/dist/quote-EW5HQA6H.js +23 -0
  43. package/dist/react.d.ts +21 -11
  44. package/dist/react.js +590 -833
  45. package/dist/scene-video-backdrop-PCTARAAJ.js +7 -0
  46. package/dist/server.d.ts +7 -8
  47. package/dist/server.js +91 -74
  48. package/dist/{system-prompt-RRXIWDDD.js → system-prompt-SYRT5DWM.js} +1 -1
  49. package/dist/template-catalog.d.ts +257 -16
  50. package/dist/template-catalog.js +1 -1
  51. package/dist/templates.d.ts +5 -5
  52. package/dist/test.d.ts +3 -3
  53. package/dist/test.js +18 -18
  54. package/dist/text-stream-LD364KBI.js +9 -0
  55. package/dist/{types-DWFO6qca.d.ts → types-CdBmNTGD.d.ts} +1 -1
  56. package/dist/{types-CEh9eUFu.d.ts → types-DdZw4GRQ.d.ts} +17 -48
  57. package/dist/validate-4OUD2CLX.js +10 -0
  58. package/docs/architecture.md +1 -1
  59. package/docs/concepts.md +1 -1
  60. package/docs/custom-templates.md +13 -28
  61. package/docs/customization.md +11 -59
  62. package/docs/getting-started.md +2 -2
  63. package/docs/immersive-interface.md +3 -4
  64. package/docs/maintainers/cinematic-migration.md +34 -0
  65. package/docs/media-and-audio.md +14 -14
  66. package/docs/motion-and-effects.md +6 -14
  67. package/docs/persistence.md +3 -3
  68. package/docs/production.md +11 -9
  69. package/docs/provider-integration.md +9 -16
  70. package/docs/reference/design-system.html +1 -1
  71. package/docs/reference/protocol.md +14 -4
  72. package/docs/testing.md +1 -1
  73. package/package.json +6 -3
  74. package/registry/items/backgrounds.json +1 -1
  75. package/registry/items/chapterTitle.json +80 -0
  76. package/registry/items/cinemaMedia.json +136 -0
  77. package/registry/items/comparison.json +101 -0
  78. package/registry/items/editorialTimeline.json +105 -0
  79. package/registry/items/focusCards.json +90 -0
  80. package/registry/items/keyFigure.json +97 -0
  81. package/registry/items/mobileMessage.json +153 -0
  82. package/registry/items/quote.json +94 -0
  83. package/registry/items/template-context.json +1 -1
  84. package/registry/items/theme.json +1 -1
  85. package/starters/video-chat/README.md +24 -26
  86. package/starters/video-chat/package.json +1 -1
  87. package/starters/video-chat/providers/video.ts +6 -5
  88. package/starters/video-chat/stock.ts +35 -151
  89. package/styles/fonts/OFL.txt +93 -0
  90. package/styles/fonts/manifest.json +66 -0
  91. package/styles/fonts/roboto-v51-cyrillic-ext.woff2 +0 -0
  92. package/styles/fonts/roboto-v51-cyrillic.woff2 +0 -0
  93. package/styles/fonts/roboto-v51-greek-ext.woff2 +0 -0
  94. package/styles/fonts/roboto-v51-greek.woff2 +0 -0
  95. package/styles/fonts/roboto-v51-latin-ext.woff2 +0 -0
  96. package/styles/fonts/roboto-v51-latin.woff2 +0 -0
  97. package/styles/fonts/roboto-v51-math.woff2 +0 -0
  98. package/styles/fonts/roboto-v51-symbols.woff2 +0 -0
  99. package/styles/fonts/roboto-v51-vietnamese.woff2 +0 -0
  100. package/styles/fonts/roboto.css +91 -0
  101. package/styles/video-chat.css +30 -22
  102. package/dist/bg-confetti-HITCGLGD.js +0 -89
  103. package/dist/bg-emoji-VU4UZYZN.js +0 -150
  104. package/dist/bg-media-L34PDQXJ.js +0 -106
  105. package/dist/brand-message-FUJ4STFO.js +0 -256
  106. package/dist/chart-bar-EGPHJATL.js +0 -310
  107. package/dist/chart-counter-5FFWQNZO.js +0 -306
  108. package/dist/chart-progress-ring-JMXQLMLC.js +0 -302
  109. package/dist/chunk-4UNKZEAT.js +0 -3575
  110. package/dist/chunk-5CDAM24P.js +0 -31
  111. package/dist/chunk-AOWSXU2K.js +0 -18
  112. package/dist/chunk-CJ7F2CE6.js +0 -104
  113. package/dist/chunk-FRN6WKHA.js +0 -185
  114. package/dist/chunk-G5JPHBX7.js +0 -336
  115. package/dist/chunk-L5BTTCZV.js +0 -97
  116. package/dist/chunk-P22HHFDK.js +0 -43
  117. package/dist/chunk-PP3MCPN4.js +0 -1998
  118. package/dist/chunk-QAAO7OAY.js +0 -19
  119. package/dist/chunk-S7HFLYNN.js +0 -35
  120. package/dist/chunk-W3VDIBGR.js +0 -217
  121. package/dist/chunk-XGRA2MUV.js +0 -242
  122. package/dist/chunk-XTOXFMLM.js +0 -105
  123. package/dist/chunk-YJJC4N4D.js +0 -761
  124. package/dist/compose-video-2NFH4DZY.js +0 -13
  125. package/dist/cta-logo-YR7LYUR5.js +0 -212
  126. package/dist/cta-media-DQJBKUO2.js +0 -221
  127. package/dist/incoming-call-54KQ7O5A.js +0 -323
  128. package/dist/infographic-before-after-ROS52GOW.js +0 -460
  129. package/dist/infographic-feature-list-E7MGUDAN.js +0 -272
  130. package/dist/infographic-problem-solution-3S6NGO5N.js +0 -343
  131. package/dist/infographic-stat-row-SYB7AB7H.js +0 -247
  132. package/dist/infographic-steps-KCE6JWE2.js +0 -406
  133. package/dist/prompt-input-MHX4O42G.js +0 -239
  134. package/dist/reaction-SLEGR3BE.js +0 -122
  135. package/dist/scene-video-backdrop-XFPX4O3I.js +0 -10
  136. package/dist/showcase-code-2HLAVKBM.js +0 -357
  137. package/dist/showcase-phone-Q4HEO4LH.js +0 -278
  138. package/dist/showcase-terminal-DL45LUFG.js +0 -312
  139. package/dist/showcase-web-2QZO73FZ.js +0 -396
  140. package/dist/social-conversation-75SD2637.js +0 -695
  141. package/dist/social-milestone-GEFUQSBF.js +0 -296
  142. package/dist/social-notification-FJ5VHVLK.js +0 -247
  143. package/dist/social-review-stack-KXQCKFRP.js +0 -242
  144. package/dist/social-testimonial-JZ7CHC7S.js +0 -287
  145. package/dist/social-tweet-UCDXM25F.js +0 -298
  146. package/dist/text-stream-54LXABJW.js +0 -10
  147. package/dist/validate-T7GBU2YF.js +0 -8
  148. package/registry/items/barChart.json +0 -245
  149. package/registry/items/beforeAfter.json +0 -180
  150. package/registry/items/bigNumber.json +0 -232
  151. package/registry/items/brandMessage.json +0 -164
  152. package/registry/items/cardList.json +0 -214
  153. package/registry/items/chatMessenger.json +0 -151
  154. package/registry/items/chatWhatsapp.json +0 -151
  155. package/registry/items/codeEditor.json +0 -191
  156. package/registry/items/confetti.json +0 -107
  157. package/registry/items/ctaLogo.json +0 -167
  158. package/registry/items/ctaMedia.json +0 -200
  159. package/registry/items/emojiBurst.json +0 -101
  160. package/registry/items/incomingCall.json +0 -177
  161. package/registry/items/media.json +0 -187
  162. package/registry/items/milestone.json +0 -212
  163. package/registry/items/notification.json +0 -189
  164. package/registry/items/phoneMockup.json +0 -261
  165. package/registry/items/problemSolution.json +0 -193
  166. package/registry/items/progressRing.json +0 -206
  167. package/registry/items/promptInput.json +0 -155
  168. package/registry/items/reaction.json +0 -176
  169. package/registry/items/reviewStack.json +0 -231
  170. package/registry/items/steps.json +0 -215
  171. package/registry/items/terminal.json +0 -219
  172. package/registry/items/testimonial.json +0 -171
  173. package/registry/items/tripleStats.json +0 -235
  174. package/registry/items/tweet.json +0 -210
  175. package/registry/items/webMockup.json +0 -280
@@ -0,0 +1,7 @@
1
+ import {
2
+ SceneVideoBackdrop
3
+ } from "./chunk-77VY4O7A.js";
4
+ import "./chunk-TVIU23OM.js";
5
+ export {
6
+ SceneVideoBackdrop
7
+ };
package/dist/server.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { m as VideoGenerationContext, n as VideoPlanner, o as VideoRequest, a as VideoCapabilities, g as VideoInput, d as VideoAudio, p as VideoSceneValidator, j as VideoTemplatePacing, q as VideoSnapshotRetention, r as VideoResumeCursor, s as VideoSceneValidationContext, e as VideoScene, b as VideoOrientation } from './types-CEh9eUFu.js';
2
- import { a as VideoFinishReason, V as VideoWarning, b as VideoEvent } from './events-BQ6z264n.js';
3
- export { c as VideoWarningCategory } from './events-BQ6z264n.js';
4
- import { S as SceneTemplateMetadata } from './catalog-types-BIhSpOWK.js';
5
- import { f as VideoChatWelcomeOptions } from './types-DWFO6qca.js';
6
- export { d as VideoChatCapabilities, g as VideoChatConversationTurn, V as VideoChatMode, h as VideoChatWelcomePrompt } from './types-DWFO6qca.js';
1
+ import { k as VideoGenerationContext, l as VideoPlanner, m as VideoRequest, a as VideoCapabilities, j as VideoInput, d as VideoAudio, n as VideoSceneValidator, g as VideoTemplatePacing, o as VideoSnapshotRetention, p as VideoResumeCursor, q as VideoSceneValidationContext, e as VideoScene, b as VideoOrientation } from './types-DdZw4GRQ.js';
2
+ import { a as VideoFinishReason, V as VideoWarning, b as VideoEvent } from './events-B4YCc4vc.js';
3
+ export { c as VideoWarningCategory } from './events-B4YCc4vc.js';
4
+ import { S as SceneTemplateMetadata } from './catalog-types-WTbLP6Jh.js';
5
+ import { f as VideoChatWelcomeOptions } from './types-CdBmNTGD.js';
6
+ export { d as VideoChatCapabilities, g as VideoChatConversationTurn, V as VideoChatMode, h as VideoChatWelcomePrompt } from './types-CdBmNTGD.js';
7
7
 
8
8
  interface VideoProviderUsage {
9
9
  inputTokens?: number;
@@ -170,8 +170,7 @@ interface VideoHandlerOptions extends Omit<VideoStreamHandlerOptions, "generate"
170
170
  *
171
171
  * Unbounded by default, which is right when media is searched for and wrong
172
172
  * when it is generated: the planner decides the scene count, and every scene
173
- * is then a paid clip. Past the ceiling a scene keeps its copy on the brand
174
- * gradient, and a `media_budget_reached` warning says it happened.
173
+ * is then a paid clip. Past the ceiling a scene uses its grounded text fallback, and a `media_budget_reached` warning says it happened.
175
174
  */
176
175
  maxResolvedMedia?: number;
177
176
  /**
package/dist/server.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createTextDeltaVideoPlanner
3
- } from "./chunk-L7QGGKJ2.js";
3
+ } from "./chunk-PDQFQIQW.js";
4
4
  import {
5
5
  createVideo,
6
6
  invokeIsolated
7
- } from "./chunk-3VRMKFM7.js";
7
+ } from "./chunk-ZDJNFA52.js";
8
8
  import {
9
9
  getGenerationLifecycleSink
10
10
  } from "./chunk-E7CL7UPB.js";
@@ -12,10 +12,11 @@ import {
12
12
  BUILTIN_SERVER_TEMPLATE_KIT,
13
13
  createServerTemplateRegistry,
14
14
  overlayServerTemplateRegistry
15
- } from "./chunk-OWBT45EN.js";
15
+ } from "./chunk-MQILEEXC.js";
16
16
  import {
17
- createTemplateSceneValidator
18
- } from "./chunk-LMG3GHLL.js";
17
+ createTemplateSceneValidator,
18
+ validateTemplateSceneStructure
19
+ } from "./chunk-IQMYK5DX.js";
19
20
  import {
20
21
  MEDIA_RECOVERY_NOTICE,
21
22
  decodeVideoSse,
@@ -23,8 +24,8 @@ import {
23
24
  sanitizeVideoChatMedia,
24
25
  videoSseHeaders,
25
26
  withDeadline
26
- } from "./chunk-KT3M7KDL.js";
27
- import "./chunk-2OKB27TG.js";
27
+ } from "./chunk-HKUI5BTB.js";
28
+ import "./chunk-RXTN2CW6.js";
28
29
  import {
29
30
  allowedKeys,
30
31
  createClippedVariableWarning,
@@ -32,28 +33,26 @@ import {
32
33
  jsonValue,
33
34
  nonNegativeInteger,
34
35
  parseVideoEvent,
36
+ parseVideoPlanPart,
35
37
  record,
36
38
  safePublicDiagnostic,
37
39
  string
38
- } from "./chunk-MMUXVA47.js";
40
+ } from "./chunk-7M56IUUX.js";
39
41
  import {
40
42
  MAX_RETAINED_MEDIA_URL_LENGTH
41
- } from "./chunk-2DOLF4QN.js";
43
+ } from "./chunk-AUPC6MDK.js";
42
44
  import {
43
45
  VIDEO_PROTOCOL_VERSION
44
- } from "./chunk-AOWSXU2K.js";
46
+ } from "./chunk-7NZOMTAL.js";
45
47
  import {
46
48
  createTemplateSystemPrompt,
47
49
  getStandardMediaResolverContract
48
- } from "./chunk-AITKH6QT.js";
49
- import "./chunk-5SLENAJW.js";
50
- import "./chunk-352ZZCET.js";
51
- import "./chunk-4UNKZEAT.js";
50
+ } from "./chunk-OR52UMSF.js";
51
+ import "./chunk-PAJ44TFW.js";
52
+ import "./chunk-EGVQODKU.js";
53
+ import "./chunk-LH4BPALA.js";
52
54
  import "./chunk-3O7OMMMF.js";
53
55
  import "./chunk-2E6T633S.js";
54
- import {
55
- validateBrandInput
56
- } from "./chunk-XGRA2MUV.js";
57
56
  import "./chunk-73NTSFFI.js";
58
57
 
59
58
  // src/server/request-validation.ts
@@ -109,7 +108,6 @@ function parseVideoRequest(value) {
109
108
  "orientation",
110
109
  "style",
111
110
  "opening",
112
- "brand",
113
111
  "personalization",
114
112
  "suppliedMedia",
115
113
  "audio"
@@ -224,7 +222,6 @@ function parseVideoRequest(value) {
224
222
  );
225
223
  }
226
224
  }
227
- if (input.brand != null) validateBrandInput(input.brand, "request.input.brand");
228
225
  jsonValue(input, "request.input");
229
226
  if (request.capabilities != null) capabilities(request.capabilities, "request.capabilities");
230
227
  if (request.resume != null) {
@@ -473,7 +470,8 @@ function fallbackVariables(variables) {
473
470
  delete fallback.mediaKeyword;
474
471
  delete fallback.mediaUrl;
475
472
  delete fallback.mediaPoster;
476
- fallback.mediaType = "gradient";
473
+ delete fallback.mediaType;
474
+ delete fallback.mediaSource;
477
475
  return fallback;
478
476
  }
479
477
  async function resolveVariables(options) {
@@ -559,7 +557,18 @@ async function resolvePartVariables(options) {
559
557
  scene: options.part.scene,
560
558
  variables: options.part.scene.variables
561
559
  });
562
- return { ...options.part, scene: { ...options.part.scene, variables } };
560
+ if (options.templateId === "cinemaMedia" && !variables.mediaUrl) {
561
+ const title = variables.fallbackText;
562
+ if (typeof title !== "string" || !title.trim() || [...title].length > 65) {
563
+ throw new Error("A media scene without a usable asset requires grounded fallbackText (1\u201365 characters)");
564
+ }
565
+ return { ...options.part, scene: { ...options.part.scene, templateId: "chapterTitle", variables: { title: title.trim() } } };
566
+ }
567
+ const rendered = { ...variables };
568
+ delete rendered.mediaSource;
569
+ delete rendered.fallbackText;
570
+ delete rendered.shotDirection;
571
+ return { ...options.part, scene: { ...options.part.scene, variables: rendered } };
563
572
  }
564
573
  return options.part;
565
574
  }
@@ -579,6 +588,25 @@ function createMediaResolvingPlanner(options) {
579
588
  }
580
589
  return false;
581
590
  };
591
+ const preflight = (part) => {
592
+ try {
593
+ parseVideoPlanPart(part);
594
+ if (part.type === "scene.add") {
595
+ const { templateId, variables } = part.scene;
596
+ if (context.request.capabilities?.templates && !context.request.capabilities.templates.includes(templateId)) {
597
+ throw new Error(`Scene template ${templateId} was not negotiated`);
598
+ }
599
+ const template = options.templates.getTemplateMetadata(templateId);
600
+ if (!template) throw new Error(`Template ${templateId} is not installed`);
601
+ validateTemplateSceneStructure(template.schema, variables, true);
602
+ }
603
+ return true;
604
+ } catch (cause) {
605
+ const error = cause instanceof Error ? cause : new Error(String(cause));
606
+ if (!getGenerationLifecycleSink(context)?.rejectPart?.(error)) throw error;
607
+ return false;
608
+ }
609
+ };
582
610
  const resolveOne = (part, openingReady2) => resolvePartVariables({
583
611
  part,
584
612
  requestId: context.request.requestId,
@@ -598,7 +626,9 @@ function createMediaResolvingPlanner(options) {
598
626
  })
599
627
  });
600
628
  if (limit === 1) {
601
- for await (const part of options.planner(context)) yield await resolveOne(part);
629
+ for await (const part of options.planner(context)) {
630
+ if (preflight(part)) yield await resolveOne(part);
631
+ }
602
632
  return;
603
633
  }
604
634
  let openingReady = options.isOpeningReady(context.request.input);
@@ -631,6 +661,7 @@ function createMediaResolvingPlanner(options) {
631
661
  const next = await iterator.next();
632
662
  if (next.done || consumerClosed) break;
633
663
  const part = next.value;
664
+ if (!preflight(part)) continue;
634
665
  pending.push(resolveOne(part, openingReady).then(
635
666
  (resolved) => ({ part: resolved }),
636
667
  (cause) => ({ cause })
@@ -680,6 +711,7 @@ function clipText(value, maxLength) {
680
711
  }
681
712
  function boundValue(value, schema) {
682
713
  if (!schema) return value;
714
+ if (schema.format === "grounded-stat" || schema.format === "grounded-quote") return value;
683
715
  if (typeof value === "string") {
684
716
  return typeof schema.maxLength === "number" ? clipText(value, schema.maxLength) : value;
685
717
  }
@@ -707,10 +739,10 @@ function createBoundedVariablePlanner(options) {
707
739
  }
708
740
  try {
709
741
  const lifecycle = getGenerationLifecycleSink(context);
710
- if (!options.templates.getTemplateMetadata(part.scene.templateId) && lifecycle?.recoverGeneratedParts && (context.request.capabilities?.templates == null || context.request.capabilities.templates.includes("media"))) {
742
+ if (!options.templates.getTemplateMetadata(part.scene.templateId) && lifecycle?.recoverGeneratedParts && (context.request.capabilities?.templates == null || context.request.capabilities.templates.includes("chapterTitle"))) {
711
743
  const copy = [part.scene.variables.texts, part.scene.variables.title, part.scene.variables.message, part.scene.narration].find((value) => typeof value === "string" && Boolean(value.trim()) && !/https?:\/\/|\b(?:api[_-]?key|authorization|secret|token|password)\s*[:=]/i.test(value));
712
744
  if (copy) {
713
- part = { ...part, scene: { ...part.scene, templateId: "media", variables: { texts: copy, mediaType: "gradient" } } };
745
+ part = { ...part, scene: { ...part.scene, templateId: "chapterTitle", variables: { title: copy } } };
714
746
  lifecycle.reportWarning?.({ code: "provider_warning", category: "provider", message: "Some scenes use a simpler layout.", recoverable: true });
715
747
  }
716
748
  }
@@ -830,30 +862,19 @@ function createVideoHandler(options) {
830
862
  }
831
863
 
832
864
  // src/server/video-chat-prompts.ts
833
- var RESPONSE_SCENES = 5;
834
- function createVideoChatResponseInstructions(fullAiVideo, openingAlreadyProvided = false, maxGeneratedVideos = 5) {
865
+ function createVideoChatResponseInstructions(generatedVideoAvailable, openingAlreadyProvided = false, maxGeneratedVideos = 5) {
835
866
  return [
836
- "The input is a prompt from a user. Respond as a short, coherent video.",
837
- "Honor the requested purpose and tone. A story should feel like a story, a recommendation should be useful, an explanation should be clear, and a creative request should not be turned into a lecture.",
838
- fullAiVideo ? 'Your first output line must be exactly one host-consumed JSON object: {"type":"video-chat.opening","spokenHook":"6-9 words","mediaKeyword":"2-4 concrete words","fallbackKeyword":"2-4 broader concrete words","firstShot":{"text":"2-7 words","narration":"10-16 spoken words","mediaKeyword":"2-8 concrete words"}}. This leading object is the one explicit exception to the standard plan-part list and is consumed before plan validation.' : openingAlreadyProvided ? "The host has already started the supplied OPENING ALREADY SPOKEN. Start directly with scene.add plan parts; do not emit a video-chat.opening object." : 'Your first output line must be exactly one host-consumed JSON object: {"type":"video-chat.opening","spokenHook":"6-9 words","mediaKeyword":"2-4 concrete words","fallbackKeyword":"2-4 broader concrete words"}. This leading object is the one explicit exception to the standard plan-part list and is consumed before plan validation.',
839
- "For the opening only, fallbackKeyword names a broader real-world subject that can provide atmosphere if the specific stock search misses. It must not pretend to demonstrate specialized instructions. For example, Minecraft castle can broaden to medieval castle for atmosphere. Do not broaden body-scene subjects into misleading demonstrations.",
840
- fullAiVideo && openingAlreadyProvided ? "Copy the supplied OPENING ALREADY SPOKEN exactly into spokenHook; do not replace or paraphrase it." : void 0,
841
- fullAiVideo ? "The host inserts firstShot as the first body scene and begins generating it immediately. After the opening object, emit exactly four additional scenes that continue it. Never emit, repeat, paraphrase, or replace firstShot as a scene." : `After the opening instruction above, use exactly ${RESPONSE_SCENES} scenes and build a progression that fits the request:`,
842
- fullAiVideo ? `The response has a budget of ${maxGeneratedVideos} generated-video attempts, including the host-inserted firstShot. The first ${maxGeneratedVideos} media requests may generate footage; all later requests use stock footage. Plan later subjects that stock footage can honestly illustrate. Keep five total scenes even when the budget is smaller; do not request extra generated scenes.` : void 0,
843
- fullAiVideo ? void 0 : "1. Open directly on the strongest image, action, claim, or idea.",
844
- "2. Develop it with new information or movement.",
845
- "3. Deepen, complicate, or advance the response.",
846
- "4. Deliver the turn, consequence, recommendation, or emotional peak.",
847
- "5. Close on the payoff or the one thing worth carrying forward, never a recap.",
848
- "No scene may repeat another scene's job. If a scene could be deleted without weakening the response, replace it.",
849
- "The prompt is already on screen, so never spend the opening scene restating it.",
850
- "If the input includes an OPENING ALREADY SPOKEN transcript, continue directly from that exact hook. Never repeat, paraphrase, contradict, or render it as the first scene's copy.",
851
- "For factual requests, distinguish supplied facts from stable general knowledge and never invent statistics, quotations, sources, or personal experience.",
852
- "For creative requests, create the requested material directly rather than explaining how one might create it.",
853
- fullAiVideo ? "Use the media template for every scene you emit. Every scene must set mediaType to video and carry a mediaKeyword: a concrete, filmable subject, a real object doing a real thing, never a diagram or an abstraction. Never use mediaType gradient and never omit mediaKeyword - a scene without one has nothing to film. If a beat seems too abstract to film, film the closest real thing that shows it." : "Choose the template that fits each beat honestly - a figure belongs in a number scene, an ordered process in steps, a comparison in a chart - rather than repeating one shape. Give any scene that would be stronger over real footage a mediaKeyword: a concrete, filmable subject, a real object doing a real thing, never a diagram or an abstraction. Use the media template for the one or two beats most worth watching happen.",
854
- fullAiVideo ? 'Mark the final scene placement:"closer". Use the media template for it, as for every other beat.' : 'Mark the final scene placement:"closer" and use either the milestone or the media template for it. Those are the only two here that may close, so the response must end on one.',
855
- "Every scene must carry a timing object, even an empty one. A scene without it fails the whole response.",
856
- "Never mention the video, the scenes, or yourself."
867
+ "Respond as a coherent short film: establish, develop, then land a useful or emotional payoff. Match the user's requested form.",
868
+ generatedVideoAvailable ? 'First emit one host-consumed opening JSON object: {"type":"video-chat.opening","spokenHook":"6-9 words","mediaKeyword":"literal subject","firstShot":{"text":"short grounded fallback","narration":"one natural spoken sentence","mediaKeyword":"2-8 concrete words","shotDirection":"optional framing and action"}}. Include firstShot only when a distinctive illustrative shot serves the story; omit it for an abstract explanation best opened with graphics. When present, the host inserts it once and begins generation immediately; do not emit it again.' : openingAlreadyProvided ? "Start directly with scene.add; the opening has already been supplied." : 'First emit {"type":"video-chat.opening","spokenHook":"6-9 words","mediaKeyword":"literal subject"}. This is host-consumed, not a scene.',
869
+ openingAlreadyProvided ? "Preserve the supplied opening exactly; continue it without repeating its words or claim." : void 0,
870
+ "Continue with only as many scenes as the story and duration need. Never pad to a fixed count. Every emitted scene carries narration beside variables and timing; no additional narration request should be needed.",
871
+ "For concrete topics, coherent footage carries the story and graphics clarify particular points. Maintain consistent setting, lighting and subject while varying shot scale. Avoid unrelated cinematic montages.",
872
+ "Use the installed catalog: parallel points, ordered events, comparison, exact quote, one key figure, chapter or a message only when its narrative job fits. Do not force every template into a video. Avoid repeating the same graphic or more than two consecutive graphic scenes unless necessary.",
873
+ `At most ${generatedVideoAvailable ? maxGeneratedVideos : 0} generated-video attempts are available. Media scenes choose mediaSource=generate for distinctive illustrative shots or mediaSource=stock for generic verified imagery. A stock miss is not permission to broaden essential details. Do not spend generation on every scene.`,
874
+ "Each mediaKeyword is a literal filmable subject/action, 2\u20138 words, maximum 80 characters. Use optional shotDirection for action, camera framing and continuity; it does not change the literal stock subject. Supply a short grounded fallbackText when the schema declares it. Do not put visible headline text over full-bleed footage.",
875
+ "No invented quotations, attribution, statistics, personal evidence or URLs. Creative stories may be invented when requested; do not present generated illustration as historical evidence.",
876
+ 'Emit exactly one final placement:"closer" scene using a catalog template with a suitable payoff or ask job. It should land the meaning, not recap the whole answer.',
877
+ "Every scene needs timing, even an empty object. Narration forms one continuous spoken explanation, uses natural sentence lengths, and does not read every on-screen word back. Finish with plan.complete."
857
878
  ].filter((line) => line != null).join("\n");
858
879
  }
859
880
  var VIDEO_CHAT_NARRATION_PROMPT = [
@@ -944,10 +965,10 @@ function boundedString2(value, label, maximum = MAX_PROMPT_CHARACTERS) {
944
965
  }
945
966
  function parseResponseRequest(value) {
946
967
  const body = record2(value, "request");
947
- allowedKeys2(body, ["prompt", "opening", "mode", "orientation", "conversation", "brand", "style"], "request");
948
- const mode = body.mode ?? "templates";
949
- if (mode !== "templates" && mode !== "full") {
950
- throw new Error("request.mode must be templates or full");
968
+ allowedKeys2(body, ["prompt", "opening", "mode", "orientation", "conversation", "style"], "request");
969
+ const mode = body.mode ?? "cinematic";
970
+ if (mode !== "cinematic") {
971
+ throw new Error("request.mode must be cinematic");
951
972
  }
952
973
  const orientation = body.orientation ?? "landscape";
953
974
  if (orientation !== "portrait" && orientation !== "landscape") {
@@ -970,7 +991,6 @@ function parseResponseRequest(value) {
970
991
  ...turn.response == null ? {} : { response: boundedString2(turn.response, `request.conversation[${index}].response`, MAX_CONVERSATION_RESPONSE_CHARACTERS) }
971
992
  };
972
993
  }),
973
- ...body.brand == null ? {} : { brand: body.brand },
974
994
  ...body.style == null ? {} : { style: body.style }
975
995
  };
976
996
  }
@@ -1015,10 +1035,11 @@ function readGeneratedFirstShot(value) {
1015
1035
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1016
1036
  const shot = value;
1017
1037
  const bounded = (field, maximum) => typeof field === "string" ? [...field.trim()].slice(0, maximum).join("").trim() : "";
1018
- const text = bounded(shot.text, 120);
1038
+ const text = bounded(shot.text, 65);
1019
1039
  const narration = bounded(shot.narration, 300);
1020
1040
  const mediaKeyword = bounded(shot.mediaKeyword, 80).match(/\S+/gu)?.slice(0, 8).join(" ") ?? "";
1021
- return text && narration && mediaKeyword ? { text, narration, mediaKeyword } : void 0;
1041
+ const shotDirection = bounded(shot.shotDirection, 220);
1042
+ return text && narration && mediaKeyword ? { text, narration, mediaKeyword, ...shotDirection ? { shotDirection } : {} } : void 0;
1022
1043
  }
1023
1044
  function boundedWords(value, maximum, characters) {
1024
1045
  if (typeof value !== "string") return "";
@@ -1044,9 +1065,11 @@ function reservedFirstScene(requestId, firstShot) {
1044
1065
  type: "scene.add",
1045
1066
  scene: {
1046
1067
  id: `${requestId}-first-shot`,
1047
- templateId: "media",
1068
+ templateId: "cinemaMedia",
1048
1069
  variables: {
1049
- texts: firstShot.text,
1070
+ fallbackText: firstShot.text,
1071
+ mediaSource: "generate",
1072
+ ...firstShot.shotDirection ? { shotDirection: firstShot.shotDirection } : {},
1050
1073
  mediaType: "video",
1051
1074
  mediaKeyword: firstShot.mediaKeyword
1052
1075
  },
@@ -1102,7 +1125,7 @@ function interceptOpeningPlan(source, options) {
1102
1125
  decided = true;
1103
1126
  if (opening) {
1104
1127
  if (!options.openingProvided) options.publish(opening.line ? opening : void 0);
1105
- if (options.fullAiVideo && opening.firstShot) {
1128
+ if (options.generatedVideoAvailable && opening.firstShot) {
1106
1129
  yield `${JSON.stringify(reservedFirstScene(options.requestId, opening.firstShot))}
1107
1130
  `;
1108
1131
  }
@@ -1123,7 +1146,7 @@ function interceptOpeningPlan(source, options) {
1123
1146
  decided = true;
1124
1147
  if (opening) {
1125
1148
  if (!options.openingProvided) options.publish(opening.line ? opening : void 0);
1126
- if (options.fullAiVideo && opening.firstShot) {
1149
+ if (options.generatedVideoAvailable && opening.firstShot) {
1127
1150
  yield `${JSON.stringify(reservedFirstScene(options.requestId, opening.firstShot))}
1128
1151
  `;
1129
1152
  }
@@ -1303,21 +1326,20 @@ function createVideoChatHandler(options) {
1303
1326
  generatedVideo: generateVideo != null,
1304
1327
  stockMedia: searchMedia != null,
1305
1328
  transcription: transcribe != null,
1306
- modes: generateVideo ? ["templates", "full"] : ["templates"]
1329
+ modes: ["cinematic"]
1307
1330
  };
1308
1331
  const welcomePrompts = (welcomeOptions?.prompts ?? DEFAULT_WELCOME_PROMPTS).slice(0, 4);
1309
1332
  const heroQuery = welcomeOptions?.heroQuery;
1310
1333
  let welcomeResponse;
1311
1334
  let requestSequence = 0;
1312
- const responseHandler = (requestedMode, requestId, openingProvided, openingChannel) => {
1313
- const mode = requestedMode !== "templates" && !generateVideo ? "templates" : requestedMode;
1314
- const fullAiVideo = mode === "full";
1335
+ const responseHandler = (requestId, openingProvided, openingChannel) => {
1336
+ const generatedVideoAvailable = generateVideo != null && maxGeneratedVideos > 0;
1315
1337
  let lifecycle;
1316
1338
  let generatedAttempts = 0;
1317
1339
  const completedVideos = /* @__PURE__ */ new Map();
1318
1340
  const reusedUrls = /* @__PURE__ */ new Set();
1319
1341
  const resolveSelected = generateVideo || searchMedia ? async (query, context) => {
1320
- const reuseKey = JSON.stringify([query, context.generatedLook ?? ""]);
1342
+ const reuseKey = JSON.stringify([query, context.generatedLook ?? "", context.scene.variables.shotDirection ?? "", context.input.orientation ?? "landscape"]);
1321
1343
  const remember = (media) => {
1322
1344
  if (media?.type === "video") completedVideos.set(reuseKey, media);
1323
1345
  return media;
@@ -1349,7 +1371,7 @@ function createVideoChatHandler(options) {
1349
1371
  return null;
1350
1372
  }
1351
1373
  };
1352
- if (fullAiVideo) {
1374
+ if (generatedVideoAvailable && context.scene.variables.mediaSource === "generate") {
1353
1375
  const generated = generatedAttempts < maxGeneratedVideos ? (generatedAttempts++, await attempt(generateVideo, generateVideoTimeoutMs)) : null;
1354
1376
  if (generated) return remember(generated);
1355
1377
  lifecycle?.reportWarning?.({
@@ -1361,7 +1383,7 @@ function createVideoChatHandler(options) {
1361
1383
  }
1362
1384
  const stock = await attempt(searchMedia, 3e3);
1363
1385
  if (stock) return remember(stock);
1364
- if (fullAiVideo) {
1386
+ if (generatedVideoAvailable) {
1365
1387
  const previous = completedVideos.get(reuseKey);
1366
1388
  if (previous && !reusedUrls.has(previous.url)) {
1367
1389
  reusedUrls.add(previous.url);
@@ -1376,10 +1398,10 @@ function createVideoChatHandler(options) {
1376
1398
  lifecycle = getGenerationLifecycleSink(context);
1377
1399
  try {
1378
1400
  return interceptOpeningPlan(videoOptions.streamText(context), {
1379
- expectOpening: fullAiVideo || !openingProvided,
1401
+ expectOpening: generatedVideoAvailable || !openingProvided,
1380
1402
  openingProvided,
1381
1403
  requestId,
1382
- fullAiVideo,
1404
+ generatedVideoAvailable,
1383
1405
  publish: openingChannel.publish
1384
1406
  });
1385
1407
  } catch (cause) {
@@ -1392,7 +1414,7 @@ function createVideoChatHandler(options) {
1392
1414
  allowCredentials,
1393
1415
  maxBodyBytes,
1394
1416
  mediaConcurrency,
1395
- basePrompt: [createVideoChatResponseInstructions(fullAiVideo, openingProvided, maxGeneratedVideos), instructions?.trim()].filter(Boolean).join("\n\nAPPLICATION GUIDANCE\n"),
1417
+ basePrompt: [createVideoChatResponseInstructions(generatedVideoAvailable, openingProvided, maxGeneratedVideos), instructions?.trim()].filter(Boolean).join("\n\nAPPLICATION GUIDANCE\n"),
1396
1418
  narrate: true,
1397
1419
  resolveMedia: resolveSelected
1398
1420
  });
@@ -1518,8 +1540,6 @@ function createVideoChatHandler(options) {
1518
1540
  } catch (cause) {
1519
1541
  return jsonError2(400, "invalid_request", cause instanceof Error ? cause.message : "Request is invalid", headers);
1520
1542
  }
1521
- const mode = input.mode !== "templates" && !generateVideo ? "templates" : input.mode;
1522
- const fullAiVideo = mode === "full";
1523
1543
  const requestId = `video-chat-${Date.now()}-${requestSequence += 1}`;
1524
1544
  const openingChannel = createOpeningChannel(input.opening ? { line: input.opening, keyword: "" } : void 0);
1525
1545
  const forwardedHeaders = new Headers(request.headers);
@@ -1529,16 +1549,14 @@ function createVideoChatHandler(options) {
1529
1549
  headers: forwardedHeaders,
1530
1550
  signal: request.signal,
1531
1551
  body: JSON.stringify({
1532
- protocolVersion: "0.5",
1552
+ protocolVersion: VIDEO_PROTOCOL_VERSION,
1533
1553
  requestId,
1534
- ...fullAiVideo ? { capabilities: { templates: ["media"] } } : {},
1535
1554
  input: {
1536
1555
  input: conversationInput(input.prompt, input.conversation, input.opening),
1537
1556
  knowledgeMode: "general",
1538
1557
  opening: false,
1539
1558
  orientation: input.orientation,
1540
1559
  maxDurationSec: 40,
1541
- ...input.brand ? { brand: input.brand } : {},
1542
1560
  style: {
1543
1561
  density: "airy",
1544
1562
  motion: "calm",
@@ -1549,7 +1567,6 @@ function createVideoChatHandler(options) {
1549
1567
  })
1550
1568
  });
1551
1569
  const response = await responseHandler(
1552
- mode,
1553
1570
  requestId,
1554
1571
  input.opening != null,
1555
1572
  openingChannel
@@ -3,7 +3,7 @@ import {
3
3
  VIDEO_PLAN_INSTRUCTION,
4
4
  createVideoSystemPrompt,
5
5
  videoPlanInstruction
6
- } from "./chunk-5SLENAJW.js";
6
+ } from "./chunk-PAJ44TFW.js";
7
7
  export {
8
8
  DEFAULT_VIDEO_SYSTEM_PROMPT,
9
9
  VIDEO_PLAN_INSTRUCTION,