@milenyumai/film-kit 2.2.1 → 2.3.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 (55) hide show
  1. package/README.md +68 -17
  2. package/build/index.d.ts +1 -1
  3. package/build/lib/cli.js +2 -2
  4. package/build/lib/film-kit.js +6 -4
  5. package/build/lib/storyboard-reference/adapters/kling30.js +3 -1
  6. package/build/lib/storyboard-reference/adapters/seedance20.d.ts +4 -0
  7. package/build/lib/storyboard-reference/adapters/seedance20.js +72 -13
  8. package/build/lib/storyboard-reference/adapters/veo31.js +3 -1
  9. package/build/lib/storyboard-reference/index.d.ts +1 -1
  10. package/build/lib/storyboard-reference/output-writer.js +84 -6
  11. package/build/lib/storyboard-reference/prompt-bundle-builder.js +295 -8
  12. package/build/lib/storyboard-reference/request-normalizer.js +8 -4
  13. package/build/lib/storyboard-reference/storyboard-interpreter.d.ts +3 -1
  14. package/build/lib/storyboard-reference/storyboard-interpreter.js +21 -1
  15. package/build/lib/storyboard-reference/types.d.ts +151 -2
  16. package/build/lib/storyboard-reference/validators.js +2 -5
  17. package/build/lib/templates.js +10 -6
  18. package/content/ARCHITECTURE.md +4 -4
  19. package/content/MASTER.md +2 -2
  20. package/content/RULES.md +4 -4
  21. package/content/agents/prompt-engineer.md +7 -7
  22. package/content/skills/prompt-structure/SKILL.md +14 -11
  23. package/content/skills/reference-locking/SKILL.md +6 -4
  24. package/content/skills/semantic-consistency/SKILL.md +1 -1
  25. package/content/skills/storyboard-reference/SKILL.md +54 -13
  26. package/content/workflows/generate-storyboard.md +37 -16
  27. package/content/workflows/generate.md +7 -7
  28. package/content/workflows/safety-check.md +2 -2
  29. package/package.json +1 -1
  30. package/packages/gpt-image-smart/content/skills/storyboard-reference/SKILL.md +104 -12
  31. package/packages/gpt-image-smart/content/workflows/generate-storyboard.md +89 -12
  32. package/packages/hybrid/content/skills/storyboard-reference/SKILL.md +104 -12
  33. package/packages/hybrid/content/workflows/generate-storyboard.md +89 -12
  34. package/packages/hybrid-smart/content/skills/storyboard-reference/SKILL.md +104 -12
  35. package/packages/hybrid-smart/content/workflows/generate-storyboard.md +89 -12
  36. package/packages/multi/build/cli.js +39 -0
  37. package/packages/multi/build/index.d.ts +1 -1
  38. package/packages/multi/build/lib/configure.js +208 -1
  39. package/packages/multi/build/lib/defaults.d.ts +3 -1
  40. package/packages/multi/build/lib/defaults.js +32 -0
  41. package/packages/multi/build/lib/templates.js +146 -60
  42. package/packages/multi/build/lib/types.d.ts +16 -0
  43. package/packages/multi/content/agents/continuity-editor.md +6 -6
  44. package/packages/multi/content/agents/delivery-editor.md +2 -2
  45. package/packages/multi/content/agents/lead-director.md +18 -10
  46. package/packages/multi/content/agents/semantic-auditor.md +4 -5
  47. package/packages/multi/content/agents/shot-generator.md +9 -27
  48. package/packages/multi/content/skills/storyboard-reference/SKILL.md +104 -12
  49. package/packages/multi/content/workflows/chain-multi.md +4 -4
  50. package/packages/multi/content/workflows/generate-multi.md +6 -6
  51. package/packages/multi/content/workflows/generate-storyboard.md +89 -12
  52. package/packages/multi/content/workflows/generate-teammate.md +8 -14
  53. package/packages/multi/content/workflows/safety-check-multi.md +7 -11
  54. package/packages/studio/content/skills/storyboard-reference/SKILL.md +104 -12
  55. package/packages/studio/content/workflows/generate-storyboard.md +89 -12
package/README.md CHANGED
@@ -55,18 +55,18 @@ After the first run, Film-Kit writes `film-kit.config.json` and uses that file a
55
55
 
56
56
  In Codex-enabled projects, all presets now include an optional `/codex-images` still phase after a successful `/generate`. It is always opt-in and never auto-starts.
57
57
 
58
- Storyboard-reference prompt generation is also available when you have a character reference image, storyboard reference image, and brief. It produces model-specific prompt bundles without requiring start/end frames.
58
+ Storyboard-reference prompt generation is also available when you have a character reference image and brief. It creates one-time GPT Image 2 character sheet prompts, per-shot GPT Image 2 storyboard prompts, and model-specific video prompt bundles without requiring start/end frames. A storyboard guide image can still be supplied, but it is optional.
59
59
 
60
60
  ## Presets
61
61
 
62
- | Preset | Purpose | Supported video model surface | Default notes |
63
- |---|---|---|---|
64
- | `single` | Single-agent prompt runtime | `veo31`, `kling-3.0`, `seedance-2.0` | Defaults to all supported platforms |
65
- | `multi` | Lead-directed multi-agent shot generation | `veo31`, `kling-3.0`, `seedance-2.0` | Defaults to `maxTeammates=5`, `batchSize=4` |
66
- | `hybrid` | Nano Banana still generation + GPT Image 2 companion metadata + fixed Kling video runtime | fixed `kling-3.0` | No `--model` flag |
67
- | `hybrid-smart` | Nano Banana still generation + GPT Image 2 companion metadata + dialogue-aware Veo/Kling routing | fixed smart route | No `--model` flag |
68
- | `gpt-image-smart` | GPT Image 2 still generation + dialogue-aware Veo/Kling routing | fixed smart route | Defaults to `size=2048x1152`, `quality=medium`, `format=png` |
69
- | `studio` | Scenario-to-render pipeline with fal.ai render-stage configuration plus GPT Image 2 companion metadata | `veo31`, `kling-3.0`, `seedance-2.0` | Includes refs dir and fal model configuration |
62
+ | Preset | Purpose | Video/model surface | Storyboard / reference support | Default notes |
63
+ |---|---|---|---|---|
64
+ | `single` | Single-agent prompt runtime | `veo31`, `kling-3.0`, `seedance-2.0` | Can store `storyboard-reference` config and ships the storyboard skill/workflow | Defaults to all supported platforms |
65
+ | `multi` | Lead-directed multi-agent shot generation | `veo31`, `kling-3.0`, `seedance-2.0` | Best fit for Seedance storyboard-reference teams: character sheets, shot storyboard prompts, team-plan metadata, handoff notes | Defaults to `maxTeammates=5`, `batchSize=4` |
66
+ | `hybrid` | Nano Banana still generation + GPT Image 2 companion metadata + fixed Kling video runtime | fixed `kling-3.0` | Ships the updated storyboard skill/workflow for prompt planning; video runtime stays Kling-focused | No `--model` flag |
67
+ | `hybrid-smart` | Nano Banana still generation + GPT Image 2 companion metadata + dialogue-aware Veo/Kling routing | fixed smart route | Ships the updated storyboard skill/workflow and GPT Image companion metadata | No `--model` flag |
68
+ | `gpt-image-smart` | GPT Image 2 still generation + dialogue-aware Veo/Kling routing | fixed smart route | GPT Image 2-first preset; includes the character-sheet + per-shot storyboard prompt contract | Defaults to `size=2048x1152`, `quality=medium`, `format=png` |
69
+ | `studio` | Scenario-to-render pipeline with fal.ai render-stage configuration plus GPT Image 2 companion metadata | `veo31`, `kling-3.0`, `seedance-2.0` | Full pipeline option for Seedance storyboard-reference plus refs dir and render-stage config | Includes refs dir and fal model configuration |
70
70
 
71
71
  ## Model Support
72
72
 
@@ -81,9 +81,9 @@ Storyboard-reference prompt generation is also available when you have a charact
81
81
  - Supported in `single`, `multi`, and `studio`
82
82
  - Runtime now includes Seedance-specific prompt guidance
83
83
  - Seedance prompt behavior emphasizes:
84
- - multimodal role assignment such as `@image1`, `@video1`, `@audio1`
85
- - first-frame anchoring like `Use @image1 as the first frame of the scene.`
86
- - continuous-shot wording such as `No scene cuts throughout, one continuous shot.`
84
+ - multimodal role assignment such as `@Image1` / `@image1`, `@video1`, `@audio1`
85
+ - first-frame anchoring like `Use @Image1 as the first frame of the scene.`
86
+ - continuous-shot wording such as `No scene cuts throughout, one continuous shot.` only for uninterrupted camera moves
87
87
  - explicit extension syntax such as `Extend @video1 by 5s`
88
88
  - separation of identity reference vs camera reference vs action reference
89
89
 
@@ -122,12 +122,34 @@ When the active model is not Kling, generated runtime files intentionally render
122
122
 
123
123
  ## Storyboard Reference Mode
124
124
 
125
- Use this mode when the source material is a character reference image plus a storyboard image rather than explicit start/end frames.
125
+ Use this mode when the source material is a character reference image plus a brief rather than explicit start/end frames. Film-Kit first prepares character-sheet prompts, then per-shot storyboard prompts. Optional storyboard images can be supplied as loose composition/timing guides.
126
+
127
+ Initialize a Seedance storyboard-reference runtime:
128
+
129
+ ```bash
130
+ npx @milenyumai/film-kit init \
131
+ --preset multi \
132
+ --model seedance-2.0 \
133
+ --reference-mode storyboard-reference \
134
+ --max-storyboard-phases 4
135
+ ```
136
+
137
+ For a GPT Image 2-first workflow, initialize the smart image preset with the same reference mode:
138
+
139
+ ```bash
140
+ npx @milenyumai/film-kit init \
141
+ --preset gpt-image-smart \
142
+ --reference-mode storyboard-reference \
143
+ --gpt-image-size 2048x1152 \
144
+ --gpt-image-quality medium \
145
+ --gpt-image-format png
146
+ ```
147
+
148
+ Generate prompt bundle files directly:
126
149
 
127
150
  ```bash
128
151
  npx @milenyumai/film-kit generate-storyboard \
129
152
  --character-ref ./refs/character.png \
130
- --storyboard-ref ./refs/storyboard.png \
131
153
  --brief ./scenario.md \
132
154
  --models veo31,seedance-2.0,kling-3.0 \
133
155
  --duration 8 \
@@ -135,20 +157,49 @@ npx @milenyumai/film-kit generate-storyboard \
135
157
  --output-dir ./outputs
136
158
  ```
137
159
 
160
+ Add an external storyboard guide only when you already have one:
161
+
162
+ ```bash
163
+ npx @milenyumai/film-kit generate-storyboard \
164
+ --character-ref ./refs/character.png \
165
+ --storyboard-ref ./refs/storyboard-guide.png \
166
+ --brief ./scenario.md \
167
+ --models seedance-2.0 \
168
+ --duration 8
169
+ ```
170
+
138
171
  Outputs:
139
172
 
140
173
  - `outputs/storyboard-reference-plan.json`
174
+ - `outputs/reference-prep/CHARACTER-SHEET-*.md`
175
+ - `outputs/storyboard-prompts/SHOTNN-GPT-IMAGE-2-STORYBOARD.md`
141
176
  - `outputs/prompt-bundles/SHOTNN.bundle.json`
142
177
  - `outputs/shots/SHOTNN.md`
143
178
  - `outputs/reports/STORYBOARD-REFERENCE-QA.md`
144
179
 
145
180
  Rules:
146
181
 
147
- - `@character1` is the identity lock.
148
- - `@storyboard1` controls composition, blocking, camera, timing, and editorial phase order only.
149
- - 5+ storyboard phases are split into multiple `SHOTNN.md` files.
182
+ - `@character1` is used once to create a 16:9 character sheet prompt with front, back, side, three-quarter, and face close-up views.
183
+ - Each `SHOTNN.md` includes a `GPT IMAGE 2 STORYBOARD PROMPT`; the generated shot storyboard controls composition, blocking, camera, timing, and editorial phase order only.
184
+ - Seedance provider-facing tokens map generated character sheets first (`@Image1`, `@Image2`, ...) and the generated shot storyboard next. Legacy lowercase aliases remain documented in runtime text.
185
+ - Phase budget: 4-6s uses 1-2 phases, 7-10s uses 2-3 phases, and 11-15s uses 3-4 phases.
186
+ - 5+ storyboard phases are split into multiple `SHOTNN.md` files by default.
187
+ - Multi-phase storyboards use planned phase transitions instead of forcing `No scene cuts throughout, one continuous shot.`
150
188
  - Existing start/end workflows stay unchanged and remain the default.
151
189
 
190
+ CLI flags for direct storyboard bundle generation:
191
+
192
+ | Flag | Description |
193
+ |---|---|
194
+ | `--character-ref` | Required character identity reference image path or URL. Repeatable or comma-separated |
195
+ | `--storyboard-ref` | Optional storyboard guide image path or URL. Repeatable or comma-separated |
196
+ | `--brief` | Inline brief text or a path to a brief/scenario file |
197
+ | `--models` | Comma-separated target models: `veo31`, `seedance-2.0`, `kling-3.0` |
198
+ | `--duration` | Duration in seconds per generated shot. Seedance provider range is 4-15 seconds |
199
+ | `--aspect` | `16:9`, `9:16`, or `1:1` |
200
+ | `--storyboard-panel-count-hint` | Optional total panel/phase hint. 5+ phases are split when overload splitting is enabled |
201
+ | `--max-storyboard-phases` | Upper bound per shot. Default: `4` |
202
+
152
203
  ## CLI
153
204
 
154
205
  Base command:
package/build/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export { configureFilmKit, detectFilmKitPreset, FILM_KIT_CONFIG_FILE, FILM_KIT_P
2
2
  export { configureAgents } from "./lib/configure.js";
3
3
  export { buildStoryboardReferencePromptBundles, normalizeVideoPromptRequest, renderShotMarkdown, writeStoryboardReferenceOutputs } from "./lib/storyboard-reference/index.js";
4
4
  export type { AgentConfigOptions, ConfigureFilmKitResult, ConfigureResult, FilmKitConfigFile, FilmKitConfigOptions, FilmKitPreset, GptImageFormat, GptImageQuality, GptImageSize, HybridAspectRatio, KlingPreset, NanoBananaImageSize, ReferenceMode, StoryboardReferenceConfig, SupportedModel, SupportedPlatform } from "./lib/types.js";
5
- export type { ModelPromptOutput, PromptBundle, ReferenceAsset, StoryboardReferenceBuildResult, StoryboardReferenceWriteResult, VideoPromptRequest } from "./lib/storyboard-reference/index.js";
5
+ export type { CharacterReferenceSheetPrompt, ModelPromptOutput, ModelRouteMetadata, PromptBundle, ProviderAssetMappingEntry, ProviderFileLimitReport, ProviderReferenceTokenPolicy, ReferenceAsset, ReferenceAssetRequirement, SeedanceContinuityMode, ShotHandoffNote, StoryboardImagePrompt, StoryboardPhaseBudget, StoryboardReferenceBuildResult, StoryboardReferencePolicy, StoryboardReferenceWriteResult, VideoPromptRequest } from "./lib/storyboard-reference/index.js";
package/build/lib/cli.js CHANGED
@@ -492,7 +492,7 @@ Usage:
492
492
  npx @milenyumai/film-kit init --preset hybrid-smart --kling-preset balanced --gpt-image-size 2048x1152
493
493
  npx @milenyumai/film-kit init --preset gpt-image-smart --gpt-image-size 2048x1152 --gpt-image-quality medium
494
494
  npx @milenyumai/film-kit init --preset studio --refs-dir ./refs --fal-video-model fal-ai/kling-video/v3/pro/image-to-video
495
- npx @milenyumai/film-kit generate-storyboard --character-ref ./refs/character.png --storyboard-ref ./refs/storyboard.png --brief ./scenario.md --models veo31,seedance-2.0,kling-3.0 --duration 8 --aspect 16:9
495
+ npx @milenyumai/film-kit generate-storyboard --character-ref ./refs/character.png --brief ./scenario.md --models veo31,seedance-2.0,kling-3.0 --duration 8 --aspect 16:9
496
496
  npx @milenyumai/film-kit init --overwrite
497
497
 
498
498
  Common flags:
@@ -513,7 +513,7 @@ Preset-specific flags:
513
513
 
514
514
  generate-storyboard flags:
515
515
  --character-ref Character identity reference image path or URL (repeatable or comma-separated)
516
- --storyboard-ref Storyboard planning reference image path or URL (repeatable or comma-separated)
516
+ --storyboard-ref Optional storyboard guide image path or URL (repeatable or comma-separated)
517
517
  --brief Inline brief text or a path to a brief file
518
518
  --models Comma-separated models: veo31,seedance-2.0,kling-3.0
519
519
  --duration Duration in seconds per generated shot
@@ -372,10 +372,12 @@ export async function configureFilmKit(options = {}) {
372
372
  merged.preset = preset;
373
373
  const runtime = await loadPresetRuntime(preset);
374
374
  const resolved = runtime.resolve(pickPresetOptions(preset, merged));
375
- assignDefined(resolved, {
376
- referenceMode: merged.referenceMode,
377
- storyboardReferenceMode: merged.storyboardReferenceMode
378
- });
375
+ if (resolved.referenceMode === undefined && merged.referenceMode !== undefined) {
376
+ resolved.referenceMode = merged.referenceMode;
377
+ }
378
+ if (resolved.storyboardReferenceMode === undefined && merged.storyboardReferenceMode !== undefined) {
379
+ resolved.storyboardReferenceMode = merged.storyboardReferenceMode;
380
+ }
379
381
  const configureOptions = pickPresetOptions(preset, resolved);
380
382
  const result = await runtime.configure(configureOptions);
381
383
  const configPath = join(rootDir, FILM_KIT_CONFIG_FILE);
@@ -22,7 +22,7 @@ export class Kling30PromptAdapter extends BaseVideoModelPromptAdapter {
22
22
  const visualWorld = input.visualWorld;
23
23
  const route = routeForPhaseCount(input.interpretation.phases.length);
24
24
  const promptText = `[CHARACTER / SUBJECT CONSISTENCY]
25
- Use the character reference as the exact identity and wardrobe anchor. Same face, hair, skin texture, body proportions, wardrobe, accessories, and visible props. The storyboard reference controls staging, camera rhythm, and phase order only. Do not inherit alternate character design from the storyboard.
25
+ Use the generated character sheet reference as the exact identity and wardrobe anchor. Same face, hair, skin texture, body proportions, wardrobe, accessories, and visible props. The generated shot storyboard reference (${input.storyboardImagePrompt.outputPath}) controls staging, camera rhythm, and phase order only. Do not inherit alternate character design from the storyboard.
26
26
 
27
27
  [CORE INTENT]
28
28
  ${input.request.brief}
@@ -60,6 +60,8 @@ identity drift, face drift, outfit drift, storyboard text, panel borders, waterm
60
60
  displayName: getDisplayName(this.model),
61
61
  routeMetadata: {
62
62
  route,
63
+ providerAssetMapping: input.providerAssetMapping,
64
+ phaseBudget: input.phaseBudget,
63
65
  phaseCount: input.interpretation.phases.length,
64
66
  maxCustomStoryboardPhases: this.maxStoryboardPhases
65
67
  },
@@ -6,6 +6,10 @@ export declare class Seedance20PromptAdapter extends BaseVideoModelPromptAdapter
6
6
  readonly supportsCharacterReference = true;
7
7
  readonly supportsAudioPlan = true;
8
8
  readonly maxStoryboardPhases = 4;
9
+ private buildProviderWarnings;
10
+ private getMappingEntries;
11
+ private formatCharacterRoleLine;
12
+ private formatStoryboardRoleLine;
9
13
  buildPrompt(input: AdapterInput): ModelPromptOutput;
10
14
  validate(output: ModelPromptOutput): ModelPromptQa;
11
15
  }
@@ -5,12 +5,56 @@ export class Seedance20PromptAdapter extends BaseVideoModelPromptAdapter {
5
5
  supportsCharacterReference = true;
6
6
  supportsAudioPlan = true;
7
7
  maxStoryboardPhases = 4;
8
+ buildProviderWarnings(fileLimits) {
9
+ const warnings = [];
10
+ if (fileLimits.observedImages > fileLimits.maxImages) {
11
+ warnings.push(`Seedance provider image reference limit exceeded: ${fileLimits.observedImages}/${fileLimits.maxImages}.`);
12
+ }
13
+ if (fileLimits.observedVideos > fileLimits.maxVideos) {
14
+ warnings.push(`Seedance provider video reference limit exceeded: ${fileLimits.observedVideos}/${fileLimits.maxVideos}.`);
15
+ }
16
+ if (fileLimits.observedAudio > fileLimits.maxAudio) {
17
+ warnings.push(`Seedance provider audio reference limit exceeded: ${fileLimits.observedAudio}/${fileLimits.maxAudio}.`);
18
+ }
19
+ if (fileLimits.observedTotalFiles > fileLimits.maxTotalFiles) {
20
+ warnings.push(`Seedance provider total reference file limit exceeded: ${fileLimits.observedTotalFiles}/${fileLimits.maxTotalFiles}.`);
21
+ }
22
+ return warnings;
23
+ }
24
+ getMappingEntries(input, role) {
25
+ return Object.values(input.providerAssetMapping).filter(entry => entry.role === role);
26
+ }
27
+ formatCharacterRoleLine(input) {
28
+ const characterEntries = this.getMappingEntries(input, "character_identity");
29
+ const tokens = characterEntries.map(entry => entry.token);
30
+ const tokenText = tokens.length === 1 ? tokens[0] : tokens.join(", ");
31
+ const aliasText = characterEntries
32
+ .map(entry => entry.legacyAlias)
33
+ .filter((alias) => Boolean(alias))
34
+ .join(", ");
35
+ return tokens.length === 1
36
+ ? `Use ${tokenText} as the exact character identity reference. Preserve the same face, hair, body proportions, wardrobe, accessories, and visible props throughout.${aliasText ? ` Legacy alias: ${aliasText}.` : ""}`
37
+ : `Use ${tokenText} as exact character identity references. Preserve each matching face, hair, body proportion, wardrobe, accessory, and visible prop throughout; never merge or swap identities.${aliasText ? ` Legacy aliases: ${aliasText}.` : ""}`;
38
+ }
39
+ formatStoryboardRoleLine(input) {
40
+ const storyboardEntry = this.getMappingEntries(input, "storyboard_plan")[0];
41
+ const token = storyboardEntry?.token ?? "@Image2";
42
+ const alias = storyboardEntry?.legacyAlias;
43
+ return `Use ${token} as the shot storyboard reference for composition, blocking, camera direction, timing, action rhythm, and phase order only. Do not copy storyboard text, labels, panel borders, watermarks, logos, or alternate character design from ${token}.${alias ? ` Legacy alias: ${alias}.` : ""}`;
44
+ }
8
45
  buildPrompt(input) {
9
46
  const audioPlan = buildAudioPlan(input);
10
- const promptText = `Use @image1 as the exact character identity reference. Preserve the same face, hair, body proportions, wardrobe, accessories, and visible props throughout.
11
- Use @image2 as the storyboard reference for composition, blocking, camera direction, and action progression only. Do not copy storyboard text, labels, panel borders, watermarks, logos, or alternate character design from @image2.
47
+ const continuityInstruction = input.continuityMode === "continuous-shot"
48
+ ? "No scene cuts throughout, one continuous shot."
49
+ : "Use only the planned storyboard phase transitions below; do not add unplanned cuts, extra scene jumps, or new locations.";
50
+ const characterEntries = this.getMappingEntries(input, "character_identity");
51
+ const storyboardEntry = this.getMappingEntries(input, "storyboard_plan")[0];
52
+ const characterTokenText = characterEntries.map(entry => entry.token).join(", ") || "@Image1";
53
+ const storyboardToken = storyboardEntry?.token ?? "@Image2";
54
+ const promptText = `${this.formatCharacterRoleLine(input)}
55
+ ${this.formatStoryboardRoleLine(input)}
12
56
 
13
- No scene cuts throughout, one continuous shot unless the storyboard phases below explicitly define internal editorial stages.
57
+ ${continuityInstruction}
14
58
 
15
59
  [CORE INTENT]
16
60
  ${input.request.brief}
@@ -18,6 +62,9 @@ ${input.request.brief}
18
62
  [STORYBOARD PHASES]
19
63
  ${formatPhaseLines(input.interpretation.phases)}
20
64
 
65
+ [GPT IMAGE 2 STORYBOARD SOURCE]
66
+ Use the generated storyboard image from ${input.storyboardImagePrompt.outputPath} as ${storyboardToken}. It has ${input.storyboardImagePrompt.panelCount} panel(s), follows the shot handoff, and is not an identity source.
67
+
21
68
  [CAMERA]
22
69
  ${input.interpretation.cameraPlan.framing}, ${input.interpretation.cameraPlan.movement}, ${input.interpretation.cameraPlan.lens}, ${input.interpretation.cameraPlan.stabilization}. Preserve ${input.interpretation.cameraPlan.screenDirection}.
23
70
 
@@ -31,23 +78,22 @@ Ambience: ${audioPlan.ambience.join(", ")}.
31
78
  Music: NONE.
32
79
 
33
80
  [CONTINUITY]
34
- Identity reference stays locked to @image1. Storyboard @image2 controls staging and visual rhythm only. Character design, wardrobe, accessories, and visible props cannot be inherited from the storyboard.
81
+ Identity reference stays locked to ${characterTokenText}. Storyboard ${storyboardToken} controls staging and visual rhythm only. Character design, wardrobe, accessories, and visible props cannot be inherited from the storyboard. Storyboard text, panel borders, watermark, logo, and alternate character design are never identity sources.
35
82
 
36
83
  Avoid: identity drift, face drift, outfit drift, storyboard text, panel borders, watermark, logo, distorted hands, rubbery motion, flicker, unnatural camera jumps.`;
37
84
  const output = {
38
85
  model: this.model,
39
86
  displayName: getDisplayName(this.model),
40
87
  routeMetadata: {
41
- assetMapping: {
42
- "@image1": "character identity reference",
43
- "@image2": "storyboard/composition/timing reference"
44
- },
45
- continuousShot: true,
88
+ continuityMode: input.continuityMode,
89
+ providerAssetMapping: input.providerAssetMapping,
90
+ phaseBudget: input.phaseBudget,
91
+ providerFileLimits: input.providerFileLimits,
46
92
  phaseCount: input.interpretation.phases.length
47
93
  },
48
94
  promptText,
49
95
  audioPlan,
50
- warnings: [],
96
+ warnings: this.buildProviderWarnings(input.providerFileLimits),
51
97
  qa: { verdict: "pass", checks: {}, issues: [] }
52
98
  };
53
99
  output.qa = this.validate(output);
@@ -55,11 +101,24 @@ Avoid: identity drift, face drift, outfit drift, storyboard text, panel borders,
55
101
  }
56
102
  validate(output) {
57
103
  const base = super.validate(output);
104
+ const continuityMode = output.routeMetadata.continuityMode;
105
+ const mapping = output.routeMetadata.providerAssetMapping ?? {};
106
+ const mappingEntries = Object.values(mapping);
107
+ const characterTokens = mappingEntries
108
+ .filter(entry => entry.role === "character_identity")
109
+ .map(entry => entry.token);
110
+ const storyboardToken = mappingEntries.find(entry => entry.role === "storyboard_plan")?.token ?? "@Image2";
111
+ const hasNoCutsRule = /No scene cuts throughout, one continuous shot/i.test(output.promptText);
58
112
  const checks = {
59
113
  ...base.checks,
60
- usesImage1Identity: /@image1 as the exact character identity reference/i.test(output.promptText),
61
- usesImage2Storyboard: /@image2 as the storyboard reference/i.test(output.promptText),
62
- continuousShot: /No scene cuts throughout, one continuous shot/i.test(output.promptText),
114
+ usesCharacterIdentityTokens: characterTokens.length > 0
115
+ && characterTokens.every(token => output.promptText.includes(token))
116
+ && /exact character identity reference/i.test(output.promptText),
117
+ usesStoryboardToken: output.promptText.includes(storyboardToken) && /shot storyboard reference/i.test(output.promptText),
118
+ blocksStoryboardIdentitySources: /Storyboard text, panel borders, watermark, logo, and alternate character design are never identity sources/i.test(output.promptText),
119
+ continuityMode: continuityMode === "continuous-shot"
120
+ ? hasNoCutsRule
121
+ : !hasNoCutsRule && /planned storyboard phase transitions/i.test(output.promptText),
63
122
  musicNone: /Music: NONE/i.test(output.promptText)
64
123
  };
65
124
  const issues = [
@@ -17,7 +17,7 @@ export class Veo31PromptAdapter extends BaseVideoModelPromptAdapter {
17
17
  const audioPlan = buildAudioPlan(input);
18
18
  const camera = input.interpretation.cameraPlan;
19
19
  const visualWorld = input.visualWorld;
20
- const promptText = `Use the uploaded character reference as the exact identity anchor throughout the shot. Preserve the same face, hair, body proportions, wardrobe, accessories, and visible props. Use the uploaded storyboard image as a visual planning reference for composition, blocking, camera angle, pacing, and intended action progression only. Do not copy storyboard text, labels, panel borders, watermarks, logos, or any alternate character design from the storyboard.
20
+ const promptText = `Use the generated character sheet reference as the exact identity anchor throughout the shot. Preserve the same face, hair, body proportions, wardrobe, accessories, and visible props. Use the generated shot storyboard image from ${input.storyboardImagePrompt.outputPath} as a visual planning reference for composition, blocking, camera angle, pacing, and intended action progression only. Do not copy storyboard text, labels, panel borders, watermarks, logos, or any alternate character design from the storyboard.
21
21
 
22
22
  Cinematic ${camera.shotType} of the referenced character in ${visualWorld.environment}. ${input.request.brief} The scene follows the storyboard's ${input.interpretation.phases.length} visual beats: ${summarizeBeats(input)}. Performance remains grounded in visible micro-behavior, with controlled eye-line changes, breath, posture, and hand tension. The character reference always wins over the storyboard if identity, wardrobe, proportions, accessories, or prop design conflict.
23
23
 
@@ -39,6 +39,8 @@ Avoid: identity drift, face drift, outfit drift, storyboard panel borders, on-sc
39
39
  displayName: getDisplayName(this.model),
40
40
  routeMetadata: {
41
41
  storyboardRole: "visual planning reference",
42
+ providerAssetMapping: input.providerAssetMapping,
43
+ phaseBudget: input.phaseBudget,
42
44
  phaseCount: input.interpretation.phases.length,
43
45
  minWordTarget: 120
44
46
  },
@@ -4,4 +4,4 @@ export { resolveAssetRoles } from "./asset-role-resolver.js";
4
4
  export { interpretStoryboard } from "./storyboard-interpreter.js";
5
5
  export { renderShotMarkdown, writeStoryboardReferenceOutputs } from "./output-writer.js";
6
6
  export { assertSafeStoryboardReferenceRequest, validateModelPromptOutput, validatePromptBundle, validateResolvedAssetRoles } from "./validators.js";
7
- export type { AdapterInput, AudioPlan, CameraPlan, ContinuityAnchors, DialogueLine, ModelPromptOutput, ModelPromptQa, NormalizedVideoPromptRequest, PromptBundle, PromptBundleQa, ReferenceAsset, ReferenceAssetRole, ReferenceLockStrength, ResolvedAssetRoles, StoryboardInterpretation, StoryboardPhase, StoryboardReferenceBuildResult, StoryboardReferencePlan, StoryboardReferenceWriteResult, VideoModelPromptAdapter, VideoPromptRequest, VisualWorld, VoiceCastEntry } from "./types.js";
7
+ export type { AdapterInput, AudioPlan, CameraPlan, CharacterReferenceSheetPrompt, ContinuityAnchors, DialogueLine, ModelPromptOutput, ModelPromptQa, ModelRouteMetadata, NormalizedVideoPromptRequest, PromptBundle, PromptBundleQa, ProviderAssetMappingEntry, ProviderFileLimitReport, ProviderReferenceTokenPolicy, ReferenceAsset, ReferenceAssetRequirement, ReferenceAssetRole, ReferenceLockStrength, ResolvedAssetRoles, SeedanceContinuityMode, ShotHandoffNote, StoryboardImagePrompt, StoryboardInterpretation, StoryboardPhase, StoryboardPhaseBudget, StoryboardReferenceBuildResult, StoryboardReferencePlan, StoryboardReferencePolicy, StoryboardReferenceWriteResult, VideoModelPromptAdapter, VideoPromptRequest, VisualWorld, VoiceCastEntry } from "./types.js";
@@ -23,6 +23,23 @@ function formatQa(bundle) {
23
23
  }
24
24
  return lines.join("\n");
25
25
  }
26
+ function formatReferenceRequirements(bundle) {
27
+ return bundle.referenceAssetRequirements
28
+ .map(requirement => {
29
+ const alias = requirement.legacyAlias ? ` / ${requirement.legacyAlias}` : "";
30
+ const path = requirement.generatedPromptPath ? ` Prompt: ${requirement.generatedPromptPath}.` : "";
31
+ return `- ${requirement.token}${alias}: ${requirement.source}, ${requirement.role}, ${requirement.required ? "required" : "optional"}. ${requirement.notes}${path}`;
32
+ })
33
+ .join("\n");
34
+ }
35
+ function formatProviderTokens(bundle) {
36
+ return Object.values(bundle.providerAssetMapping)
37
+ .map(entry => {
38
+ const alias = entry.legacyAlias ? ` / ${entry.legacyAlias}` : "";
39
+ return `- ${entry.token}${alias}: ${entry.description}`;
40
+ })
41
+ .join("\n");
42
+ }
26
43
  export function renderShotMarkdown(bundle) {
27
44
  const interpretation = bundle.storyboardInterpretation;
28
45
  const audioPlan = getAudioPlan(bundle);
@@ -33,24 +50,44 @@ export function renderShotMarkdown(bundle) {
33
50
  return `# ${bundle.shotId} | ${bundle.durationSeconds}s | Mode: storyboard-reference
34
51
 
35
52
  ## Türkçe Özet
36
- Storyboard referansı kompozisyon, bloklama, kamera ve zamanlama planı olarak kullanılır; karakter referansı kimlik, kıyafet, oran, aksesuar ve görünür prop kilidi olarak korunur.
53
+ Önce tek seferlik karakter sheet referansları üretilir. Bu shot için ayrıca GPT Image 2 ile profesyonel storyboard görsel promptu üretilir; Seedance tarafında karakter sheet tokenları kimliği, shot storyboard tokenı ise kompozisyon, bloklama, kamera ve zamanlamayı yönetir.
37
54
 
38
55
  ## Model Control
39
56
  - Reference Mode: storyboard-reference
40
57
  - Target Models: ${Object.keys(bundle.modelPrompts).join(", ")}
41
58
  - Aspect Ratio: ${bundle.aspectRatio}
42
59
  - Duration: ${bundle.durationSeconds}s
60
+ - Continuity Mode: ${bundle.continuityMode}
61
+ - Phase Budget: ${bundle.phaseBudget.policy}; effective max ${bundle.phaseBudget.effectiveMaxStoryboardPhases}
43
62
  - Safety Mode: fail-closed
44
63
 
45
64
  ## Input Asset Roles
46
65
  ${Object.values(bundle.assetRoles).map(role => `- ${role.token}: ${role.role}, ${role.lockStrength} lock`).join("\n")}
47
66
 
67
+ ## Reference Asset Requirements
68
+ ${formatReferenceRequirements(bundle)}
69
+
70
+ ## Provider Reference Tokens
71
+ ${formatProviderTokens(bundle)}
72
+
73
+ ## GPT Image 2 Storyboard Prompt
74
+ - Output: ${bundle.storyboardImagePrompt.outputPath}
75
+ - Panel count: ${bundle.storyboardImagePrompt.panelCount}
76
+ - Previous handoff: ${bundle.storyboardImagePrompt.previousShotHandoff}
77
+ - Next handoff: ${bundle.storyboardImagePrompt.nextShotHandoff}
78
+
79
+ \`\`\`text
80
+ ${bundle.storyboardImagePrompt.promptText}
81
+ \`\`\`
82
+
48
83
  ## Storyboard Interpretation
49
84
  - Editorial function: ${interpretation.editorialFunction}
50
85
  - Panel/phase count: ${interpretation.panelCount}
51
86
  - Camera plan: ${interpretation.cameraPlan.framing}, ${interpretation.cameraPlan.movement}, ${interpretation.cameraPlan.lens}
52
87
  - Screen direction: ${interpretation.cameraPlan.screenDirection}
53
88
  - Light source: ${interpretation.visualWorld.lighting}
89
+ - Entry handoff: ${bundle.handoffNote.entryHandoff}
90
+ - Exit handoff: ${bundle.handoffNote.exitHandoff}
54
91
  - Continuity anchors: ${[
55
92
  ...interpretation.continuityAnchors.identity,
56
93
  ...interpretation.continuityAnchors.wardrobe,
@@ -70,6 +107,34 @@ ${modelPrompts}
70
107
  ${formatQa(bundle)}
71
108
  `;
72
109
  }
110
+ function renderCharacterSheetPrompt(prompt) {
111
+ return `# ${prompt.id}
112
+
113
+ - Provider: ${prompt.provider}
114
+ - Aspect Ratio: ${prompt.aspectRatio}
115
+ - Source: ${prompt.sourceToken}
116
+ - Generated Once: ${prompt.generatedOnce ? "YES" : "NO"}
117
+ - Cell Count: ${prompt.cellCount}
118
+
119
+ \`\`\`text
120
+ ${prompt.promptText}
121
+ \`\`\`
122
+ `;
123
+ }
124
+ function renderStoryboardPromptFile(bundle) {
125
+ return `# ${bundle.shotId} GPT Image 2 Storyboard Prompt
126
+
127
+ - Provider: ${bundle.storyboardImagePrompt.provider}
128
+ - Aspect Ratio: ${bundle.storyboardImagePrompt.aspectRatio}
129
+ - Panel Count: ${bundle.storyboardImagePrompt.panelCount}
130
+ - Character Sheets: ${bundle.storyboardImagePrompt.characterSheetIds.join(", ")}
131
+ - External Storyboard Guides: ${bundle.storyboardImagePrompt.externalStoryboardGuideIds.join(", ") || "none"}
132
+
133
+ \`\`\`text
134
+ ${bundle.storyboardImagePrompt.promptText}
135
+ \`\`\`
136
+ `;
137
+ }
73
138
  function renderQaReport(result) {
74
139
  const models = result.request.targetModels.join(", ");
75
140
  const rows = result.bundles
@@ -82,7 +147,7 @@ function renderQaReport(result) {
82
147
  .map(row => `| ${row.shotId} | ${row.model} | ${row.verdict.toUpperCase()} | ${row.notes} |`)
83
148
  .join("\n");
84
149
  const assetRows = result.bundles
85
- .map(bundle => `| ${bundle.shotId} | ${result.assets.characters.length} | ${result.assets.storyboards.length} | ${bundle.qa.verdict.toUpperCase()} |`)
150
+ .map(bundle => `| ${bundle.shotId} | ${result.characterReferenceSheetPrompts.length} | ${result.assets.storyboards.length} | ${bundle.storyboardImagePrompt ? "YES" : "NO"} | ${bundle.qa.verdict.toUpperCase()} |`)
86
151
  .join("\n");
87
152
  const issues = result.bundles.flatMap(bundle => bundle.qa.issues);
88
153
  return `# Storyboard Reference QA Report
@@ -90,12 +155,16 @@ function renderQaReport(result) {
90
155
  ## Summary
91
156
  - Verdict: ${issues.length === 0 ? "PASS" : "FAIL"}
92
157
  - Shots checked: ${result.bundles.length}
158
+ - Character sheet prompts: ${result.characterReferenceSheetPrompts.length}
159
+ - Shot storyboard prompts: ${result.bundles.length}
93
160
  - Models checked: ${models}
94
161
  - Split required: ${result.plan.splitRequired ? "YES" : "NO"}
162
+ - Phase budget: ${result.plan.policy.storyboard_reference.recommended_phase_budget.policy}
163
+ - Provider files within limits: ${result.plan.policy.storyboard_reference.provider_file_limits.withinLimits ? "YES" : "NO"}
95
164
 
96
165
  ## Asset Role Checks
97
- | Shot | Character Ref | Storyboard Ref | Verdict |
98
- |---|---:|---:|---|
166
+ | Shot | Character Sheets | External Storyboard Guides | Generated Storyboard | Verdict |
167
+ |---|---:|---:|---:|---|
99
168
  ${assetRows}
100
169
 
101
170
  ## Model Grammar Checks
@@ -122,17 +191,26 @@ export async function writeStoryboardReferenceOutputs(result, rootDir = process.
122
191
 
123
192
  - Mode: storyboard-reference
124
193
  - Character references: ${result.assets.characters.length}
125
- - Storyboard references: ${result.assets.storyboards.length}
194
+ - Character sheet prompts: ${result.characterReferenceSheetPrompts.length}
195
+ - External storyboard guides: ${result.assets.storyboards.length}
196
+ - Generated shot storyboard prompts: ${result.bundles.length}
126
197
  - Target models: ${result.request.targetModels.join(", ")}
127
198
  - Safety context: ${result.request.safetyContext}
128
199
  `);
129
200
  written.push(projectInfoPath);
201
+ for (const prompt of result.characterReferenceSheetPrompts) {
202
+ const promptPath = resolve(rootDir, prompt.outputPath);
203
+ await writeText(promptPath, renderCharacterSheetPrompt(prompt));
204
+ written.push(promptPath);
205
+ }
130
206
  for (const bundle of result.bundles) {
131
207
  const shotPath = join(outputRoot, "shots", `${bundle.shotId}.md`);
132
208
  const bundlePath = join(outputRoot, "prompt-bundles", `${bundle.shotId}.bundle.json`);
209
+ const storyboardPromptPath = resolve(rootDir, bundle.storyboardImagePrompt.outputPath);
210
+ await writeText(storyboardPromptPath, renderStoryboardPromptFile(bundle));
133
211
  await writeText(shotPath, renderShotMarkdown(bundle));
134
212
  await writeText(bundlePath, `${JSON.stringify(bundle, null, 2)}\n`);
135
- written.push(shotPath, bundlePath);
213
+ written.push(storyboardPromptPath, shotPath, bundlePath);
136
214
  }
137
215
  await writeText(qaPath, renderQaReport(result));
138
216
  written.push(qaPath);