import type { AestheticProfile } from '../storyboard/prompt-builder.js'; export interface SeriesState { name: string; slug: string; concept: string; genre: string; setting: string; projectType?: 'film' | 'series' | 'product-video' | 'music-video' | 'screenplay'; aesthetic: AestheticProfile | null; aestheticSeed?: number; characters: Character[]; /** First-class location entities with generated reference images. */ locations?: Location[]; episodes: EpisodeMeta[]; videoDefaults: VideoModelDefaults; /** * The reasoning model behind the project. Separate from `videoDefaults` * because it makes none of the pixels -- it decides what gets made. * Absent on projects created before 2.9.0, which fall back to the default. */ intelligence?: IntelligenceDefaults; storyboardAspectRatio?: '16:9' | '9:16' | '1:1'; outputDir: string; createdAt: string; updatedAt: string; } /** * Which model develops the workshop, writes the script, and reads panels back * during QA. `visionModel` equals `model` unless the chosen model cannot see, * in which case it is a companion from the same privacy tier -- see * `resolveIntelligence` in `venice/text-models.ts`. */ export interface IntelligenceDefaults { model: string; visionModel: string; } export interface VideoModelDefaults { actionModel: string; atmosphereModel: string; characterConsistencyModel?: string; /** * Model for multi-shot generation units. Defaults to * `DEFAULT_MULTISHOT_MODEL` (Seedance 2.5 R2V) — the same reference-first * lane as singles, so a multi-beat sequence keeps the full @Image slot plan * across its internal cuts. Set explicitly (e.g. to * `kling-o3-pro-image-to-video`) only when you deliberately want another * family's native multi-shot format and accept its reference limitations. */ multiShotModel?: string; /** * Paired image-generation defaults. When the video family is Seedance 2.0, * Venice blocks requests that include images produced by any other family, * so the image defaults must match the video family. */ imageDefaults?: ImageModelDefaults; /** * Strategy when an incompatible (non-seedream) image is about to be sent * to a Seedance model. Defaults to `prompt` in interactive shells and * `fallback` in non-TTY environments. */ seedanceCompatibility?: SeedanceCompatibilityMode; /** * Exact lip-sync model for dialogue shots whose character is a non-narrator * with a visible face. Consulted only when `audioStrategy === 'lip-sync'`. * Native dialogue stays on the selected R2V family and uses voice-donor * references when supported. Defaults to `resolveLipSyncModel(family)`: * the family's own audio-driven R2V lane where one exists, otherwise * `wan-2-7-image-to-video`. */ lipSyncModel?: string; /** * Auto-keyframe the lip-sync render from a Seedance R2V pass instead of the * panel image. Applies only to lip-sync models with no * `reference_image_urls` capability (Wan 2.7 i2v), whose only identity * anchor is the single `image_url` keyframe. A panel-derived keyframe * drifts mid-clip because the panel was generated without strong character * anchoring. When this flag is true (the default), such a shot first * renders a quick Seedance R2V pass (no audio, all character refs), * extracts frame 1, and uses that frame as the keyframe — locking identity * from frame 0 while the dialogue MP3 drives the mouth. Doubles per-shot * cost (~$0.85 total). Skip per-shot with * `ShotScript.disableSeedanceKeyframe = true`. * * Reference-capable lip-sync models skip the pre-pass entirely. * * See AGENTS.md rule 32 for the underlying motivation. */ seedanceKeyframeForWan?: boolean; /** * Operator's answer to "lip-sync mode or native model voices?" from the * upfront questionnaire. See AudioStrategy for semantics. When unset, the * harness uses the per-call defaults (effectively 'native'). Setting this * at series-creation time eliminates the double-narration / mouth-out-of- * sync class of bugs we hit during the PNW field-guide episode. */ audioStrategy?: AudioStrategy; /** * Operator's answer to "which video model family?" from the upfront * questionnaire. See VideoFamilyPreference. `auto` (or unset) keeps the * harness's Seedance 2.5 defaults. Setting this swaps the action / * atmosphere / character-consistency model defaults to the chosen family. */ videoFamilyPreference?: VideoFamilyPreference; /** * Auto-generate + attach a per-character voice-donor reference clip * (`reference_audio_urls`, bound in-prompt as @AudioN) on dialogue shots * that route to a reference-audio-capable model (Seedance 2.0 R2V family, * HappyHorse 1.1 R2V). The clip locks the character's voice — timbre, * accent, pacing — across shots so the native model dialogue doesn't drift * take to take. Defaults to `true`. Set `false` to disable series-wide; * `generate-videos --no-voice-reference` disables for one run. See * AGENTS.md rule 40. */ voiceReferenceForDialogue?: boolean; /** * Montage-first generation (Seedance 2.5 branch default: `true`). When on, * the planner groups each scene's consecutive beats into ONE long * single-pass generation (up to 30s on Seedance 2.5) prompted with a * timestamped SEQUENCE beat list (the "Make a full trailer with * Seedance 2.5" grammar), then the montage cutter slices the render at * every beat boundary into per-shot clips organized in the episode's * `media-library/scene-NN/` directory. Set `false` to fall back to the * 2.0-era per-shot / 15s multi-shot planning. */ montageMode?: boolean; /** * Model for montage units. Defaults to `DEFAULT_MONTAGE_MODEL` * (`seedance-2-5-reference-to-video`, 4-30s, up to 30 image refs). */ montageModel?: string; /** * Ceiling for a single montage generation in seconds. Defaults to 30 * (Seedance 2.5's single-pass max). Scenes whose beats exceed this split * into multiple montage units. */ montageMaxDurationSec?: number; /** * What happens to a montage render after the cutter has sliced it: * - `true` — auto-edit: the harness also assembles the cut shots into * the scene/episode edit automatically (the "fable with * harness" lane; assemble-episode picks the per-shot clips * up like any other shots). * - `false` — (default) library-only: the per-shot clips are provided in * `media-library/scene-NN/`, organized by scene and shot, * for a human (or the Venice Video Creator) to cut. Nothing * is assembled without an explicit assemble-episode run. * CLI: `generate-videos --auto-edit` / `--no-auto-edit` override per run. */ autoEdit?: boolean; /** * Auto-generate composed storyboard blocking plates per scene beat during * `workshop-episode` and `generate-videos`. Default `false` (2026-08-13): * the reference-first path anchors spatial consistency with coherent * location angles (all derived from one wide plate — see * `location-generator.ts`) plus the shot's authored text `blocking` * (rule 49). A blocking plate is a full pictorial frame; feeding it as an * R2V reference drags every shot in a beat toward the plate's composition * ("too similar" drift), and its composited version of the location is a * fourth, conflicting environment signal. Plates remain available on demand * via `generate-storyboard-refs` (and are still consumed as a PROTECTED * slot when present on disk) for dense multi-character blocking the text * geometry can't disambiguate. Set `true` to restore the old auto-plate * behaviour series-wide. */ useStoryboardPlates?: boolean; } export interface ImageModelDefaults { /** Image generation model (t2i) — e.g. `seedream-v5-lite`, `nano-banana-pro`. */ generationModel: string; /** Multi-edit model — e.g. `seedream-v5-lite-edit`, `nano-banana-pro-edit`. */ editModel: string; /** * Strategy for the negative-prompt builder when constructing character * reference images and panels: * - 'auto' — (default) infer from the series aesthetic. Aesthetics * mentioning "photoreal", "photograph", "documentary", * "photo", "live action", "cinematic photography" suppress * the anti-photoreal guards; everything else keeps them. * - 'stylized' — always inject anti-photoreal guards (photorealistic, * photograph, photo, 3D render, Pixar). Legacy behaviour. * - 'photoreal'— never inject anti-photoreal guards; keep only structural * guards (deformed, watermark, text, etc.). * - 'none' — emit an empty negative prompt; trust positives only. * Useful for paths that have already hand-tuned a negative * via `negativePromptOverride`. */ negativePromptStrategy?: 'auto' | 'stylized' | 'photoreal' | 'none'; } export type SeedanceCompatibilityMode = 'prompt' | 'fallback' | 'launder'; /** * How dialogue reaches the final mix. * * - 'native' — the video model speaks the dialogue in-frame. Seedance * and HappyHorse use character voice-donor references when * available to preserve timbre, accent, and pacing. Best when * characters speak only once or twice, the model's voice * range suffices, and you don't need precise control. * `assemble-episode` keeps `dialogueReplace: false`. * - 'lip-sync' — exact lip-sync mode: Venice TTS renders each dialogue * line and the video model receives it as `audio_url`, so * the character's mouth follows that exact recording. * Best when a character speaks many times (so a single * voice picks up across the episode), the user wants * accent control, or the dialogue needs deterministic * delivery. The planner routes face-visible low/medium- * motion dialogue shots to `videoDefaults.lipSyncModel`. * `assemble-episode` defaults `dialogueReplace: true`. * - 'narrator-vo' — the speaker is a NARRATOR / voice-over only (no on-camera * speaking mouth). Every dialogue-bearing shot is queued * with `audio: false` so Seedance can't synthesize a * competing narrator; Venice TTS owns the dialogue lane. * `assemble-episode` defaults `dialogueReplace: true` and * `nativeVolume: 0`. Sets `audioMix.suppressModelNarration: true`. */ export type AudioStrategy = 'native' | 'lip-sync' | 'narrator-vo'; /** * Operator's preferred video model family for action / atmosphere shots. * `auto` keeps the current defaults (Seedance 2.5). Picking a family swaps * `actionModel`, `atmosphereModel`, and `characterConsistencyModel` to that * family's i2v / R2V variants. `lipSyncModel` remains available for the * explicit exact-audio lip-sync strategy; it does not affect native dialogue. * * Family quick reference: * - 'seedance' — Seedance 2.5 (default). Strong R2V identity anchoring, * every integer 4-30s in a single pass, up to 30 reference * images, 480p/720p, native `audio: true` + reference audio. * Rides the montage-first lane. (2.0 R2V Enhanced, the * prior default, is 1080p-capable and still selectable via * a videoDefaults override.) * - 'happyhorse' — HappyHorse 1.1 (Alibaba, #1 blind-preference T2V + I2V). * Joint single-pass video+audio, phoneme-level lip-sync in * 7 languages, and R2V with up to 9 reference images. 3-15s * natives, 720p/1080p. Best for talking characters and * multilingual localization; SFW/commercial-leaning (for * mature work prefer Seedance 2.0 or Wan 2.7). The 1.0 IDs * remain in the registry for back-compat. * - 'minimax-h3' — MiniMax H3, the open-weight omni-modal model. Renders * 2K with native stereo audio for roughly a third of what * other families cost per second, and its R2V lane takes * the same 9-image reference stack as Seedance. Two hard * constraints: 2K is the only resolution (no draft tier, * so every take is a finish-quality spend) and the * duration ladder starts at 5s, so 3-4s beats have to be * re-scripted or routed elsewhere. * - 'minimax-h3-max' / 'minimax-h3-max-turbo' * — MiniMax H3 Max. Despite the name, NOT a bigger H3: it * renders 768P (not 2K), it is `private` rather than * anonymized, and it wants a plain prompt instead of the * directorial stack (`promptStyle: 'simple'`). The model * composes its own coverage and cutting from a stated * intent, which makes it the montage / "let the model tell * the beat" family — and at $0.024/s (Turbo $0.012/s, * the cheapest lane here) a 15s take is disposable enough * to generate several and pick. Duration ladder is 5-15s * like H3. Turbo has no R2V lane, so both families anchor * identity on `minimax-h3-max-reference-to-video`. * - 'wan-3-0' — Wan 3.0. The only family that renders past 15s: the * ladder runs 5/10/15/20/25/30s at 480p/720p/1080p with * native audio always on. Its R2V lane takes the same * 9-image reference stack as Seedance. It accepts no * audio input at all, so exact lip-sync shots fall back * to Wan 2.7. * - 'grok-imagine' — Grok Imagine i2v + R2V (R2V durations stepped at * 5s/8s/10s only). Pick for atmosphere-rich shots or * when the user wants Grok's signature look. * - 'kling-o3' — Kling O3 Standard / Pro / 4K. Best for stylized / * illustrated aesthetics. Accepts non-seedream images. */ export type VideoFamilyPreference = 'auto' | 'seedance' | 'happyhorse' | 'minimax-h3' | 'minimax-h3-max' | 'minimax-h3-max-turbo' | 'wan-3-0' | 'grok-imagine' | 'kling-o3'; /** * Returns the model-id triplet for a given preferred family. Used by * `createSeries` to populate `actionModel` / `atmosphereModel` / * `characterConsistencyModel` from the operator's questionnaire answer. * * `lipSyncModel` is intentionally NOT included. It is only consulted when * `audioStrategy === 'lip-sync'`; native dialogue remains on the selected * family and uses voice references when that family supports them. */ export declare function resolveVideoFamilyDefaults(family: VideoFamilyPreference): { actionModel: string; atmosphereModel: string; characterConsistencyModel: string; }; /** * Recommended `seedanceCompatibility` mode given an image-generation model. * Used by `saveSeries` to auto-fill the field when the operator hasn't * explicitly set it. The table is intentionally conservative: models known * to produce face-bearing images Seedance will accept get `prompt` (run a * fast preflight, but expect success); known-bad pairings get `fallback` * (auto-switch to a Kling fallback); unknowns get `launder` (rewrite the * image through Seedream before sending to Seedance). */ export declare const SEEDANCE_COMPATIBILITY_BY_IMAGE_MODEL: Record; export declare function recommendedSeedanceCompatibility(generationModel: string | undefined): SeedanceCompatibilityMode | undefined; export interface Character { name: string; gender: 'male' | 'female' | 'other'; age: string; description: string; fullDescription: string; wardrobe: string; voiceDescription: string; voiceId?: string; voiceName?: string; baseTraits?: string; /** * Path (relative to the series output dir or absolute) to a short * voice-donor clip used as a `reference_audio_urls` entry (bound in-prompt * as @AudioN) so the character's voice — timbre, accent, pacing — stays * consistent across shots on reference-audio-capable video models * (Seedance 2.0 R2V family, HappyHorse 1.1 R2V). Generated via * `generate-voice-reference` (default source: seed-audio-1-0 from * `voiceDescription`) or supplied by the operator via * `lock-character --voice-reference`. Convention: * `characters//voice-reference.mp3`. See AGENTS.md rule 40. */ voiceReferencePath?: string; /** Model that produced the voice reference (e.g. `seed-audio-1-0`), or `user-supplied`. */ voiceReferenceModel?: string; locked: boolean; seed: number; } export interface Location { /** Display name, e.g. "Sietch Workshop". */ name: string; /** Filesystem-safe slug; also the directory name under locations/. */ slug: string; /** Locked prose description of the environment (drives panel + ref prompts). */ description: string; /** * Lighting notes carried into every panel prompt for this location so * consecutive shots in the same place stay lit consistently (anti-pattern 7). */ lightingNotes?: string; /** * Locked spatial geography of the location: the named landmarks and their * fixed positions relative to each other (e.g. "bar counter runs along the * north wall; entrance door opposite it; window with neon sign to the left * of the door as seen from the counter"). Carried into every panel, plate, * and video prompt for shots tagged with this location so character/object * placement can be expressed relative to STABLE anchors instead of vague * prose — the geography must never rearrange between shots (rule 49). */ spatialAnchors?: string; /** * Optional time-of-day / weather variants keyed by label * (e.g. { "night": "…", "dawn": "…" }). Reserved for future per-shot * variant selection; the base `description` is used when unset. */ timeVariants?: Record; /** Deterministic seed so the reference angles stay reproducible. */ seed: number; /** Image-generation model used for the reference angles (default nano-banana-pro). */ referenceModel?: string; } export interface StoryboardReference { /** Filesystem-safe slug; also the file stem under storyboards//. */ slug: string; /** * Prose description of the moment: who is where, doing what, with what. * Should be SPATIALLY explicit — name each character's position relative to * the location's spatialAnchors and to each other (left/right of frame, * foreground/background, facing direction), so the composed plate encodes * unambiguous geometry for the video model to follow. */ description: string; /** Character names composed into the plate (drives face refs at gen time). */ characters: string[]; /** Location slug the moment takes place in (drives the env ref at gen time). */ location?: string; /** Episode this beat belongs to. */ episode: number; /** Shot numbers (or suffixed ids like "3b") this plate anchors. */ shotIds: Array; /** Deterministic seed for reproducible regeneration. */ seed: number; /** Image model used to compose the plate. */ referenceModel?: string; } /** * @deprecated Use Character instead. Kept for backward compatibility. */ export type MiniDramaCharacter = Character; export interface EpisodeMeta { number: number; title: string; status: 'draft' | 'scripted' | 'storyboarded' | 'produced' | 'assembled'; } export interface EpisodeScript { episode: number; title: string; seriesName: string; totalDuration: string; status?: 'draft' | 'approved'; shots: ShotScript[]; /** * First-class locations introduced by this episode's script (from * workshop-episode). Merged into SeriesState.locations on save so their * reference images can be generated once and reused across shots/episodes. */ locations?: Location[]; /** * Composed storyboard blocking plates for this episode's key beats * (see StoryboardReference). Planned during workshop/storyboard, generated * per beat, and referenced by shots via ShotScript.storyboardRef. */ storyboardRefs?: StoryboardReference[]; /** * Optional per-act music cues. When set, the assembler renders each cue * and ffmpeg-crossfades between adjacent cues at their fade points. The * single static music-bed path on the assembler options is kept for * back-compat — when both are present, cues win. */ musicCues?: MusicCueSpec[]; /** * Audio-mix defaults for this episode. Overrides the assembler's built-in * defaults. Optional — sensible -16 LUFS targeting is applied when omitted. */ audioMix?: AudioMixDefaults; } /** * Per-act music cue. References shot ids by **string** so that suffixed * inserts like "3b" / "3c" can be addressed without coercion bugs. The * assembler converts shot-id → start/end seconds via the placementMap * built during segment iteration. */ export interface MusicCueSpec { /** * Shot id at which this cue starts. Numeric shot numbers (e.g. `6`) or * the suffixed string form (`"3b"`) are both accepted; the assembler * normalizes via the same path builder it uses for dialogue placement. */ startShot: number | string; /** Shot id at which this cue ends (inclusive). */ endShot: number | string; /** Prompt for the music generation model. */ prompt: string; /** Music model id. Defaults to `elevenlabs-music`. */ model?: string; /** Output gain in dB. Defaults to -22. */ gain?: number; /** * Optional time-varying gain stops for a single cue. Each stop says "by the * time we reach shot `atShot`, ramp gain to `gainDb`." Stops are ramped with * a smooth volume crossfade `rampSec` seconds long (default 2.0). Stops * outside the cue's [startShot, endShot] window are ignored. When supplied, * this layers on top of the base `gain`. * * Example: "drop -20% by the time of the florida porch shot" * { startShot: 1, endShot: 10, gain: -22, * gainStops: [{ atShot: 7, gainDb: -24, rampSec: 3 }] } */ gainStops?: Array<{ atShot: number | string; gainDb: number; rampSec?: number; }>; /** Fade-in in seconds. Defaults to 1.0. */ fadeIn?: number; /** Fade-out in seconds. Defaults to 1.5. */ fadeOut?: number; /** * How this music cue behaves over the underlying score: * - 'sustain' — flat bed (default) * - 'swell' — ramp +4 dB across the cue * - 'drop' — duck to -inf for the cue's range * - 'stinger' — 0.4s pulse +6 dB then return to bed * Per-shot `shot.musicHold` automation is layered on top of this. */ musicHold?: 'sustain' | 'swell' | 'drop' | 'stinger'; /** * Optional pre-rendered audio file. When set, the assembler skips the * generation step and uses this directly. Useful for music beds that * were rendered by other harnesses or hand-edited. */ audioPath?: string; } /** * Episode-level audio-mix defaults. */ export interface AudioMixDefaults { /** Cap any SFX clip to this many seconds. Defaults to 2.0. */ sfxMaxDurationSec?: number; /** Fade-out applied after the SFX trim. Defaults to 0.3. */ sfxFadeOutSec?: number; /** Dialogue track gain in dB. Defaults to 0. */ dialogueGainDb?: number; /** Music bed gain in dB. Defaults to -22. */ musicGainDb?: number; /** SFX track gain in dB. Defaults to -16. */ sfxGainDb?: number; /** Final-pass integrated loudness target. Defaults to -16 LUFS. */ lufsTarget?: number; /** Final-pass true peak target. Defaults to -1 dBTP. */ truePeakDb?: number; /** * When true, every shot that has dialogue is queued at Seedance / Wan with * `audio: false` so the model doesn't synthesize its own narrator on top of * the Venice TTS that will be mixed in by the assembler. Strongly recommended * whenever the script's primary speaker is `NARRATOR` — Seedance i2v with * `audio: true` will eagerly generate a competing English narration track * when the prompt contains "narrator" / "documentary" / "naturalist". When * unset, the buildVideoPrompt heuristic forces `audio: false` for NARRATOR * shots anyway (since there's nothing on-camera to lip-sync to). */ suppressModelNarration?: boolean; } export type ShotEnvironment = 'DAY_INTERIOR' | 'DAY_EXTERIOR' | 'NIGHT_INTERIOR' | 'NIGHT_EXTERIOR'; export declare const DAYTIME_ENVIRONMENTS: Set; export declare const INTERIOR_ENVIRONMENTS: Set; export interface ShotScript { shotNumber: number; type: 'establishing' | 'dialogue' | 'action' | 'reaction' | 'insert' | 'close-up'; duration: string; videoModel: 'action' | 'atmosphere'; environment?: ShotEnvironment; description: string; panelDescription?: string; characters: string[]; /** * Characters visible as silhouettes/distant figures but not requiring R2V * identity anchoring. Included in panel prompts but don't trigger R2V routing. * Example: a silhouetted figure in a doorway for an establishing shot. */ silhouetteCharacters?: string[]; /** * Slug of the Location this shot takes place in (see SeriesState.locations). * When set and the location has generated reference images, the storyboard * folds the location's wide/medium ref into the panel generation and the * video generator folds it into scene_image_urls (Kling O3 R2V) or * reference_image_urls + an @ImageN env tag (Seedance / HappyHorse). */ location?: string; /** * Slug of the StoryboardReference (composed blocking plate) for this shot's * scene beat. When set and the plate exists on disk, the video generator * appends it to reference_image_urls with an @ImageN blocking role clause — * PROTECTED in the budget allocator (dropped last, after extra character * angles and extra location angles). Set by the beat planner during * storyboarding or by hand. */ storyboardRef?: string; /** * Explicit spatial blocking for the shot: where each character/object is * relative to the location's fixed anchors (Location.spatialAnchors), to * each other, and to the frame — plus facing/eyeline direction. One or two * sentences of concrete geometry, e.g. "MARA at the bar counter, screen * left, facing right toward the door; JAX enters through the door in the * background, screen right, walking toward her." * * Authored by the workshop/script LLM (or by hand) and injected verbatim * into panel, blocking-plate, and video prompts so placement is stated the * same way in every generation instead of being re-inferred per shot. * Continuity rule: keep screen direction and geography consistent with the * previous shot in the same scene unless the cut deliberately crosses the * line (rule 49). */ blocking?: string; dialogue: { character: string; line: string; delivery?: string; } | null; sfx: string | null; cameraMovement: string; transition: string; trimStart?: number; trimEnd?: number; flip?: boolean; allowMultiShot?: boolean; mustStaySingle?: boolean; continuityPriority?: 'identity' | 'continuity' | 'balanced'; /** * Per-shot motion intensity. Drives planner routing between: * - Wan 2.7 i2v (lip-sync) for low/medium-motion dialogue shots, and * - Seedance R2V (identity preservation, no lip-sync) for high motion. * * Defaults to `'medium'` when unset. Camera prompt suggestions: * - 'low' -> slow push-in, subtle parallax, still hold * - 'medium' -> gentle tracking, lateral pan * - 'high' -> tracking action, dynamic camera, whip pan */ motion?: 'low' | 'medium' | 'high'; /** * Whether the character's face is visible in the shot. Used by the * planner to decide if lip-sync makes sense. When false, dialogue-bearing * shots can stay on Seedance because there's no mouth to animate. */ faceVisible?: boolean; titleOverlay?: { text: string; fadeInSec?: number; holdSec?: number; }; episodeWardrobe?: Record; skipRefine?: boolean; useElements?: boolean; useReferenceImages?: boolean; sceneImagePaths?: string[]; /** Describes what the scene reference image should visually contribute (used in Pass 3 multi-edit). */ sceneRefDescription?: string; /** Negative prompt appended during video generation for this shot. */ negativePrompt?: string; /** Audio URL to use as background audio input for models that support it. */ audioUrl?: string; /** Video URL to use as reference input for models that support it. */ videoUrl?: string; /** * When true, skip the automatic Seedance R2V → Wan 2.7 keyframe pipeline * for this shot even if it routes to the lip-sync model. Use when you * have a specific reason to prefer the panel as the Wan 2.7 keyframe * (e.g. you've manually retouched the panel for this shot). Default * undefined → the series-level `videoDefaults.seedanceKeyframeForWan` * (default `true`) decides. */ disableSeedanceKeyframe?: boolean; /** * Per-shot music-cue automation. Layered on top of the containing * `MusicCueSpec.musicHold`. Set when a story beat (reveal, lightbulb * moment, drop) needs audio emphasis at this shot. */ musicHold?: 'sustain' | 'swell' | 'drop' | 'stinger'; /** * How the assembler should treat the video model's native (Seedance / Wan) * audio track during dialogue replacement: * - 'mute' — multiply native by 0 (silenced; only Venice TTS audible) * - 'duck' — multiply native by 0.2 (legacy default; keeps ambient bed) * - 'keep' — multiply native by 1.0 (no ducking; competes with TTS) * Per-shot value wins over the CLI's `--native-volume`. Use when one shot * has genuine ambient (paper rustle, room tone) you want to preserve while * the rest of the episode mutes a competing AI narrator. */ nativeAudio?: 'mute' | 'duck' | 'keep'; /** * Optional suffix letter for inserted shots. When set, the canonical * shot id becomes `shotNumber + shotIdSuffix` — for example, shotNumber 3 * with shotIdSuffix "b" → "3b" → key "003b". Inserted shots use this so * the order of the original shotNumbers is preserved. */ shotIdSuffix?: string; } /** * `multishot` is the current multi-shot unit type (model comes from * `resolveMultiShotModel`, default Seedance R2V Enhanced). `kling-multishot` * is the legacy name, still accepted when reading old generation-plan.json * files. */ export type GenerationUnitType = 'single' | 'multishot' | 'kling-multishot' | 'montage'; export type StartFrameStrategy = 'panel' | 'previous-last-frame'; export type EndFrameStrategy = 'natural' | 'next-panel-target'; export interface GenerationUnitSegment { shotNumber: number; startOffsetSec: number; durationSec: number; outputFile: string; } /** * A montage unit's planned beat: one shot's window inside the single * 30-second Seedance 2.5 generation. `startSec`/`endSec` are the timestamps * written into the prompt's SEQUENCE block (`[0:03-0:05] ...`) and are ALSO * the cut points the montage cutter uses afterwards — the prompt and the * edit can never disagree because they come from the same list. */ export interface MontageBeat { shotNumber: number; startSec: number; endSec: number; } export interface GenerationUnit { unitId: string; unitType: GenerationUnitType; shotNumbers: number[]; outputFile: string; model: string; duration: string; startFrameStrategy: StartFrameStrategy; endFrameStrategy: EndFrameStrategy; decisionReasons: string[]; fallbackToSingles: boolean; renderedDurationSec?: number; segments?: GenerationUnitSegment[]; /** * When true, render the keyframe via Seedance R2V first and use it as the * Wan 2.7 `image_url`. Set by the planner when the unit routes to the * lip-sync model on a single-character dialogue shot. See AGENTS.md * rule 32. */ useSeedanceKeyframe?: boolean; /** Model used for the Seedance keyframe stage when `useSeedanceKeyframe`. */ keyframeModel?: string; /** * Montage units only: the timestamped beat map for the single-pass * generation. Written into the prompt's SEQUENCE block and consumed by the * montage cutter to slice the rendered clip at the exact beat boundaries. */ montageBeats?: MontageBeat[]; /** * Montage units only: 1-based scene index within the episode. Cut clips * land in the media library under `media-library/scene-NN/`. */ sceneNumber?: number; } export interface GenerationPlan { episode: number; generatedAt: string; units: GenerationUnit[]; } export declare const DEFAULT_ACTION_MODEL = "seedance-2-5-reference-to-video"; export declare const DEFAULT_ATMOSPHERE_MODEL = "seedance-2-5-reference-to-video"; export declare const DEFAULT_CHARACTER_CONSISTENCY_MODEL = "seedance-2-5-reference-to-video"; export declare const KLING_R2V_MODEL = "kling-o3-standard-reference-to-video"; /** * Default model for multi-shot units (2026-08-07): Seedance 2.5 R2V — the SAME * reference-first lane as every other shot. Multi-beat sequences render as ONE * Seedance native multi-shot generation with `Lens switch.` separators * (rule 21), anchored to the full @Image slot plan (character sheets, blocking * plate, location angles), so identity AND geography hold across the internal * cuts. This is the lane used only when montage-first is disabled * (`videoDefaults.montageMode: false`); otherwise scenes ride the montage lane * (`DEFAULT_MONTAGE_MODEL`, also Seedance 2.5). * * The old default, `kling-o3-pro-image-to-video`, had NO `elements` and NO * `reference_image_urls` support — every multi-shot unit silently dropped all * identity anchoring (the anti-pattern 1 trap). It remains available as an * explicit `videoDefaults.multiShotModel` override only. */ export declare const DEFAULT_MULTISHOT_MODEL = "seedance-2-5-reference-to-video"; /** * @deprecated The Kling i2v multi-shot lane is no longer the default * (2026-08-05) — multi-shot units render on `DEFAULT_MULTISHOT_MODEL` * (Seedance R2V Enhanced, reference-first). Kept resolvable for projects * that explicitly set `videoDefaults.multiShotModel` to it. */ export declare const KLING_MULTISHOT_MODEL = "kling-o3-pro-image-to-video"; /** Resolve the model multi-shot units render on. */ export declare function resolveMultiShotModel(videoDefaults?: Pick): string; export declare const DEFAULT_MONTAGE_MODEL = "seedance-2-5-reference-to-video"; /** Seedance 2.5 single-pass ceiling (every integer 4-30s at quote). */ export declare const DEFAULT_MONTAGE_MAX_DURATION_SEC = 30; /** Minimum montage generation length; below this a plain single is cheaper. */ export declare const MONTAGE_MIN_DURATION_SEC = 4; /** Montage-first is the default on this branch. */ export declare function resolveMontageMode(videoDefaults?: Pick): boolean; /** * Auto-generate storyboard blocking plates? Off by default (2026-08-13) — * the reference-first path relies on coherent location angles + text blocking. * Plates are opt-in series-wide (`videoDefaults.useStoryboardPlates: true`) or * on demand via `generate-storyboard-refs`. */ export declare function resolveUseStoryboardPlates(videoDefaults?: Pick): boolean; export declare function resolveMontageModel(videoDefaults?: Pick): string; /** * Montage window ceiling, in seconds. * * This used to return a flat 30 — Seedance 2.5's single-pass max — while * accepting `montageModel` and ignoring it. Any montage model with a shorter * ladder (MiniMax H3 Max tops out at 15s) therefore got 30s windows planned * against it, and every one of those units failed `assertShotDurationsValid` * after the plan was already written. The model's own ceiling now bounds the * window, and an explicit `montageMaxDurationSec` is clamped to it rather than * overriding it into an invalid request. */ export declare function resolveMontageMaxDurationSec(videoDefaults?: Pick): number; /** * Shortest montage generation the montage model will actually accept. * * `MONTAGE_MIN_DURATION_SEC` (4s) is the Seedance floor; MiniMax H3 Max starts * at 5s and hard-400s below it. Windows under this go out as plain singles * instead, and unit durations are floored here. */ export declare function resolveMontageMinDurationSec(videoDefaults?: Pick): number; /** * Auto-edit toggle: `true` → the harness assembles the cut automatically; * `false` (default) → cut clips are provided in the media library only, * organized by scene and shot (the Venice Video Creator lane). */ export declare function resolveAutoEdit(videoDefaults?: Pick): boolean; /** * Fallback exact-lip-sync model, used when the project's chosen family has no * audio-driven lane of its own. Wan 2.7 i2v inherits aspect ratio from the * input image and follows the exact supplied `audio_url`, but it takes no * reference images, so it needs the keyframe pre-pass in rule 32. * * Prefer `resolveLipSyncModel(family)` over reading this directly. */ export declare const DEFAULT_LIP_SYNC_MODEL = "wan-2-7-image-to-video"; /** * Exact-lip-sync model for a family, consulted only when * `audioStrategy === 'lip-sync'` on visible, low/medium-motion single-speaker * dialogue. Native dialogue never routes through here. * * Families whose own R2V lane accepts a top-level `audio_url` stay in-family, * which is both cheaper and more faithful than leaving the family for Wan 2.7: * the R2V lane keeps the full reference stack, so identity is anchored from * frame 0 and the two-stage keyframe pre-pass (rule 32) is unnecessary. * Everything else falls back to Wan 2.7 i2v. */ export declare function resolveLipSyncModel(family: VideoFamilyPreference): string; /** * Does this lip-sync model need the Seedance R2V keyframe pre-pass (rule 32)? * * Only models with no `reference_image_urls` lane do. Their single identity * anchor is the `image_url` keyframe, and a panel-derived keyframe drifts * mid-clip. A reference-capable lip-sync model already carries the full * reference stack into the render, so the extra pass is wasted spend. */ export declare function lipSyncModelNeedsKeyframe(modelId: string): boolean; /** * Default image models for ALL panels — character-bearing and faceless alike. * * Historical note: Seedance 2.0 used to reject face-bearing input images that * weren't produced by `seedream-v5-lite`, so the harness forced seedream on any * panel with a character. **Venice removed that cross-family restriction (2026-07)** * — Seedance now accepts face-bearing images from any image family — so a single * high-quality default is used everywhere. `nano-banana-2` is the global default. */ export declare const DEFAULT_IMAGE_GENERATION_MODEL = "nano-banana-2"; export declare const DEFAULT_IMAGE_EDIT_MODEL = "nano-banana-2-edit"; /** * @deprecated Venice removed the Seedance seedream-only face restriction (2026-07). * These constants are retained only for backward-compatible imports; the harness * no longer forces seedream on face-bearing panels. Use * `DEFAULT_IMAGE_GENERATION_MODEL` / `DEFAULT_IMAGE_EDIT_MODEL` instead. */ export declare const SEEDANCE_FACE_GENERATION_MODEL = "seedream-v5-lite"; export declare const SEEDANCE_FACE_EDIT_MODEL = "seedream-v5-lite-edit"; /** * @deprecated Venice removed the Seedance face-image family restriction (2026-07). * Seedance now accepts face-bearing images from any image family; these sets are * kept only so older imports keep compiling. */ export declare const SEEDANCE_COMPATIBLE_GENERATION_MODELS: Set; export declare const SEEDANCE_COMPATIBLE_EDIT_MODELS: Set; /** True when the model id belongs to the Seedance 2.0 family. */ export declare function isSeedanceVideoModel(modelId: string): boolean; /** * Atmosphere/i2v fallback when the user is on a Seedance default but the * images in the request are not Seedance-compatible (or the user is in a * region where Seedance is unavailable). */ export declare const SEEDANCE_FALLBACK_ATMOSPHERE_MODEL = "veo3.1-fast-image-to-video"; export declare const SEEDANCE_FALLBACK_R2V_MODEL = "kling-o3-standard-reference-to-video"; export declare const VIDEO_NO_MUSIC_SUFFIX = "No background music. Only generate dialogue, ambient sound, and sound effects."; export declare const MODELS_SUPPORTING_ELEMENTS: Set; export declare const MODELS_SUPPORTING_REFERENCE_IMAGES: Set; export declare const MODELS_SUPPORTING_SCENE_IMAGES: Set; export declare const MODELS_SUPPORTING_END_IMAGE: Set; export declare const MODELS_USING_IMAGE_TAGS: Set; export declare const MODELS_SUPPORTING_AUDIO_INPUT: Set; /** * Models that accept per-reference `audio_url` inside `elements[]`. * * Wan 2.7 R2V is the only one today. Each `elements[].audio_url` drives a * different speaker's lip-sync inside a single render — useful for * multi-character speaking scenes. NOT interchangeable with the global * `audio_url` field used by the i2v / t2v variants. */ export declare const MODELS_SUPPORTING_PER_REFERENCE_AUDIO: Set; /** * Models that accept `reference_audio_urls` — voice-donor clips bound * in-prompt as @Audio1, @Audio2, … to keep a character's voice (timbre, * accent, pacing) consistent across shots. Up to 3 clips, 2-15s each, * ≤15s aggregate, wav/mp3, ≤15MB per file, and Venice REQUIRES at least * one reference image alongside them (audio-only is rejected at * validation). Mirror of `MODELS_SUPPORTING_AUDIO_INPUT` — kept here as a * fast lookup set for the video generator. Confirmed live via /video/quote * (HTTP 200) 2026-07-23 on all four; these do NOT set `audio_input: true` * in GET /models, so reference audio is a separate capability from the * lip-sync `audio_url` lane. */ export declare const MODELS_SUPPORTING_REFERENCE_AUDIO: Set; /** * Per-model reference_image_urls budget. The Venice API cap is 9 (per the * venice-video SKILL.md params table); models not listed here fall back to * the legacy conservative cap of 4. The old universal `.slice(0, 4)` was a * harness convention, NOT the API limit — Seedance 2.0 R2V and HappyHorse * 1.1 R2V both accept up to 9 flat reference images, which is what makes the * full reference stack (character sheets + multi-angle locations + storyboard * blocking plates) possible. */ export declare const MAX_REFERENCE_IMAGES_BY_MODEL: Record; export declare const DEFAULT_MAX_REFERENCE_IMAGES = 4; export declare function getMaxReferenceImages(modelId: string): number; export interface VideoElement { frontalImageUrl?: string; referenceImageUrls?: string[]; videoUrl?: string; /** * Per-reference audio for Wan 2.7 R2V (`per_reference_audio: true`). * When set, this element's character lip-syncs to the supplied audio * while other characters in the same render stay silent. NOT used by * models that lack `MODELS_SUPPORTING_PER_REFERENCE_AUDIO`. * * Pass as a data URL or a local file path — `audioPath` is preferred so * the audio pre-flight pad can run. */ audioUrl?: string; audioPath?: string; } export declare const FEMALE_BASE_TRAITS = "beautiful, elegant, detailed features"; export declare const MALE_BASE_TRAITS = "handsome, strong features, detailed features"; //# sourceMappingURL=types.d.ts.map