/**
 * @module _shared/scene-build
 *
 * v0.60 one-shot driver: read STORYBOARD.md (with frontmatter + per-beat
 * cues from C2), dispatch the AI primitives the cues call for, run
 * `compose-scenes-with-skills`, then optionally render to MP4.
 *
 * The intent is to make the storyboard the single source of truth — `vibe
 * scene build` walks it and produces an MP4. Per-beat cues drive TTS +
 * image generation; project frontmatter sets defaults. CLI flags override.
 *
 * Idempotent: assets that already exist on disk are reused unless `force`.
 *
 * Scope held tight for v0.60:
 *   - TTS via `resolveTtsProvider` (ElevenLabs / Kokoro auto-fallback)
 *   - T2I via OpenAI, Gemini, or Grok
 *   - No Whisper transcribe step (compose handles its own)
 *   - No root `index.html` synthesis — driver expects the project to
 *     already have one with sub-composition references.
 */
import { type ImageOptions } from "@vibeframe/ai-providers";
import { type ComposeEffort, type ComposeProgressEvent, type ComposeScenesActionResult } from "./compose-scenes-skills.js";
import type { ComposerProvider } from "./composer-resolve.js";
import { type ComposePromptsBeat } from "./compose-prompts.js";
import { type SceneRenderResult } from "./scene-render.js";
import { type BuildPlanResult, type BuildStage } from "./build-plan.js";
import { type AssetFreshness } from "./build-asset-metadata.js";
import { type JobRecord } from "./status-jobs.js";
import { type SceneRepairResult } from "./scene-repair.js";
import { type TtsProviderName } from "./tts-resolve.js";
import { type SceneBuildMode } from "./scene-build-mode.js";
export { resolveSceneBuildMode, type SceneBuildMode } from "./scene-build-mode.js";
export type BuildImageProvider = "openai" | "gemini" | "grok";
export type SceneBuildProgressEvent = {
    type: "phase-start";
    phase: "primitives" | "compose" | "render";
} | {
    type: "narration-cached";
    beatId: string;
    path: string;
} | {
    type: "narration-generated";
    beatId: string;
    path: string;
    provider: string;
} | {
    type: "narration-failed";
    beatId: string;
    error: string;
} | {
    type: "narration-skipped";
    beatId: string;
    reason: string;
} | {
    type: "backdrop-cached";
    beatId: string;
    path: string;
} | {
    type: "backdrop-generated";
    beatId: string;
    path: string;
    provider: string;
} | {
    type: "backdrop-failed";
    beatId: string;
    error: string;
} | {
    type: "backdrop-skipped";
    beatId: string;
    reason: string;
} | {
    type: "video-cached";
    beatId: string;
    path: string;
} | {
    type: "video-generated";
    beatId: string;
    path: string;
    provider: string;
} | {
    type: "video-pending";
    beatId: string;
    jobId: string;
    provider: string;
} | {
    type: "video-failed";
    beatId: string;
    error: string;
} | {
    type: "video-skipped";
    beatId: string;
    reason: string;
} | {
    type: "music-cached";
    beatId: string;
    path: string;
} | {
    type: "music-generated";
    beatId: string;
    path: string;
    provider: string;
} | {
    type: "music-pending";
    beatId: string;
    jobId: string;
    provider: string;
} | {
    type: "music-failed";
    beatId: string;
    error: string;
} | {
    type: "music-skipped";
    beatId: string;
    reason: string;
} | ComposeProgressEvent | {
    type: "render-start";
} | {
    type: "render-done";
    outputPath: string;
};
export type PrimitiveStatus = "generated" | "cached" | "referenced" | "pending" | "skipped" | "failed" | "no-cue";
export interface BeatBuildOutcome {
    beatId: string;
    narrationStatus: PrimitiveStatus;
    narrationPath?: string;
    narrationError?: string;
    narrationDurationSec?: number;
    sceneDurationSec?: number;
    narrationText?: string;
    narrationVoice?: string;
    narrationProvider?: string;
    narrationCachePath?: string;
    narrationCacheKey?: string;
    narrationMetadataPath?: string;
    narrationFreshness?: AssetFreshness;
    narrationSourcePath?: string;
    backdropStatus: PrimitiveStatus;
    backdropPath?: string;
    backdropError?: string;
    backdropPrompt?: string;
    backdropProvider?: string;
    backdropCachePath?: string;
    backdropCacheKey?: string;
    backdropMetadataPath?: string;
    backdropFreshness?: AssetFreshness;
    backdropSourcePath?: string;
    videoStatus: PrimitiveStatus;
    videoPath?: string;
    videoJobId?: string;
    videoError?: string;
    videoPrompt?: string;
    videoProvider?: string;
    videoCachePath?: string;
    videoCacheKey?: string;
    videoMetadataPath?: string;
    videoFreshness?: AssetFreshness;
    videoSourcePath?: string;
    musicStatus: PrimitiveStatus;
    musicPath?: string;
    musicJobId?: string;
    musicError?: string;
    musicPrompt?: string;
    musicProvider?: string;
    musicCachePath?: string;
    musicCacheKey?: string;
    musicMetadataPath?: string;
    musicFreshness?: AssetFreshness;
    musicSourcePath?: string;
    musicDurationSec?: number;
}
export type BuildVideoProvider = "seedance" | "grok" | "kling" | "runway" | "veo";
export type BuildMusicProvider = "elevenlabs" | "replicate";
/**
 * Build mode dispatch (Phase H3 / Plan H).
 *
 * - `agent` — host agent (Claude Code, Cursor, Codex, Aider …) is the
 *   sole reasoner. The CLI runs primitives + render, but skips its own
 *   LLM compose call. If any `compositions/scene-<id>.html` is missing,
 *   `vibe scene build` returns a structured "needs author" plan from
 *   `getComposePrompts()` and exits successfully — the host agent is
 *   expected to fill the missing files and re-invoke. Otherwise lint +
 *   render proceed.
 * - `batch` — current internal-LLM path (PR #176, multi-provider). The
 *   CLI calls Claude / OpenAI / Gemini directly to produce HTML. Right
 *   choice for CI, headless automation, and "no agent host" contexts.
 * - `auto` (default) — pick `agent` when (a) `VIBE_BUILD_MODE=agent`
 *   forces it, OR (b) any agent host is detected via
 *   `detectedAgentHosts()`. Falls back to `batch`.
 */
export interface SceneBuildOptions {
    /** Project directory containing STORYBOARD.md, DESIGN.md, index.html. */
    projectDir: string;
    /**
     * Build mode dispatch. See {@link SceneBuildMode}. Default: `auto`.
     */
    mode?: SceneBuildMode;
    /** Compose effort tier — passed through to `compose-scenes-with-skills`. */
    effort?: ComposeEffort;
    /**
     * Composer LLM provider. Defaults to whatever `resolveComposer()` picks
     * based on env keys (claude > gemini > openai). Pass an explicit value
     * to require that provider's key.
     */
    composer?: ComposerProvider;
    skipNarration?: boolean;
    skipBackdrop?: boolean;
    skipRender?: boolean;
    /** Override frontmatter providers.tts. Defaults to "auto". */
    ttsProvider?: TtsProviderName;
    /** Voice override (TTS-provider-specific id). */
    voice?: string;
    /** Override frontmatter providers.image. */
    imageProvider?: BuildImageProvider;
    /** Video provider for per-beat `video` cues. */
    videoProvider?: BuildVideoProvider;
    /** Music provider for per-beat `music` cues. */
    musicProvider?: BuildMusicProvider;
    /** Skip AI video generation even when beats declare video cues. */
    skipVideo?: boolean;
    /** Skip music generation even when beats declare music cues. */
    skipMusic?: boolean;
    /** OpenAI image quality — see `vibe generate image --quality`. */
    imageQuality?: "standard" | "hd";
    /** OpenAI image size. Default 1536x1024 for cinematic 16:9-ish framing. */
    imageSize?: ImageOptions["size"];
    /** Force re-dispatch even when the asset already exists. */
    force?: boolean;
    /** Stage to run. `all` preserves the historical full build behavior. */
    stage?: BuildStage;
    /** Restrict asset/compose work to one beat id where supported. */
    beatId?: string;
    /** Hard USD cap checked before provider spend. */
    maxCostUsd?: number;
    /** Compose-scenes cache override (tests). */
    cacheDir?: string;
    /** Progress callback. */
    onProgress?: (e: SceneBuildProgressEvent) => void;
}
/**
 * Resulting state after dispatch. `phase` makes the agent contract
 * explicit:
 *   - `done` — render succeeded, MP4 at {@link outputPath}.
 *   - `compose-only` — `--skip-render` was set; compositions written.
 *   - `needs-author` — agent mode and one or more `compositions/*.html`
 *     missing. {@link composePrompts} carries the plan the host agent
 *     needs to author. Re-invoke `vibe scene build` after writing.
 *   - `failed` — primitives, compose, or render errored. {@link error}
 *     carries the message; {@link beats} reflects partial state.
 */
export type SceneBuildPhase = "done" | "assets-only" | "pending-jobs" | "compose-only" | "sync-only" | "render-only" | "needs-author" | "failed";
export type BuildWorkflowStatus = "done" | "running" | "needs-author" | "failed" | "ready";
export type BuildCurrentStage = "assets" | "compose" | "sync" | "render" | "done";
export interface BuildBeatSummary {
    total: number;
    assetsReady: number;
    compositionsReady: number;
    needsAuthor: string[];
}
export interface StageReport {
    status: "pending" | "skipped" | "done" | "failed" | "needs-author" | "pending-jobs";
    costUsd: number;
    warnings: string[];
    retryWith: string[];
}
export type BuildSceneRepairStage = "compose" | "sync";
export interface BuildSceneRepairSummary {
    ran: boolean;
    stage: BuildSceneRepairStage | null;
    status: "skipped" | SceneRepairResult["status"];
    score: number | null;
    fixed: SceneRepairResult["fixed"];
    remainingIssues: SceneRepairResult["remainingIssues"];
    retryWith: string[];
}
export interface SceneBuildResult {
    success: boolean;
    /** Final phase reached — see {@link SceneBuildPhase}. */
    phase: SceneBuildPhase;
    /** Mode the dispatcher actually ran (after auto-resolve). */
    mode: "agent" | "batch";
    code?: string;
    error?: string;
    message?: string;
    suggestion?: string;
    recoverable?: boolean;
    validation?: BuildPlanResult["validation"];
    providerResolution?: BuildPlanResult["providerResolution"];
    beats: BeatBuildOutcome[];
    /** MP4 path when `skipRender` is false and render succeeded. */
    outputPath?: string;
    composeData?: ComposeScenesActionResult["data"];
    renderResult?: SceneRenderResult;
    /**
     * Populated only in agent mode when {@link phase} === `"needs-author"`.
     * The host agent should consume this to write each beat's HTML, then
     * re-run `vibe scene build`.
     */
    composePrompts?: {
        skillReference: string | null;
        designReference: string;
        storyboardReference: string;
        compositionsDir: string;
        instructions: string[];
        beats: ComposePromptsBeat[];
        bundleVersion: string;
        warnings: string[];
    };
    selectedStage?: BuildStage;
    status?: BuildWorkflowStatus;
    currentStage?: BuildCurrentStage;
    beatSummary?: BuildBeatSummary;
    estimatedCostUsd?: number;
    costUsd?: number;
    stageReports?: Record<"assets" | "compose" | "sync" | "render", StageReport>;
    sceneRepair?: BuildSceneRepairSummary;
    jobs?: JobRecord[];
    warnings?: string[];
    retryWith?: string[];
    reportPath?: string;
    /** Wall-clock total. */
    totalLatencyMs: number;
}
export declare function executeSceneBuild(opts: SceneBuildOptions): Promise<SceneBuildResult>;
export type BuildReport = ReturnType<typeof toBuildReport>;
declare function toBuildReport(projectDir: string, result: SceneBuildResult): {
    schemaVersion: string;
    kind: string;
    project: string;
    phase: SceneBuildPhase;
    status: BuildWorkflowStatus;
    currentStage: BuildCurrentStage;
    mode: "agent" | "batch";
    selectedStage: BuildStage;
    success: boolean;
    code: string | undefined;
    error: string | undefined;
    message: string | undefined;
    suggestion: string | undefined;
    recoverable: boolean | undefined;
    validation: {
        ok: boolean;
        issues: import("./storyboard-edit.js").StoryboardValidationIssue[];
    } | undefined;
    providerResolution: import("./build-plan.js").ProviderResolution[];
    estimatedCostUsd: number;
    costUsd: number;
    beats: {
        id: string;
        startSec: number;
        endSec: number;
        sceneDurationSec: number;
        narration: {
            text: string | undefined;
            voice: string | undefined;
            provider: string | undefined;
            path: string | undefined;
            sourcePath: string | undefined;
            durationSec: number | undefined;
            sceneDurationSec: number;
            status: PrimitiveStatus;
            error: string | undefined;
            cachePath: string | undefined;
            cacheKey: string | undefined;
            metadataPath: string | undefined;
            freshness: AssetFreshness | undefined;
        };
        backdrop: {
            prompt: string | undefined;
            provider: string | undefined;
            path: string | undefined;
            sourcePath: string | undefined;
            status: PrimitiveStatus;
            error: string | undefined;
            cachePath: string | undefined;
            cacheKey: string | undefined;
            metadataPath: string | undefined;
            freshness: AssetFreshness | undefined;
        };
        video: {
            prompt: string | undefined;
            provider: string | undefined;
            path: string | undefined;
            sourcePath: string | undefined;
            status: PrimitiveStatus;
            jobId: string | undefined;
            error: string | undefined;
            cachePath: string | undefined;
            cacheKey: string | undefined;
            metadataPath: string | undefined;
            freshness: AssetFreshness | undefined;
        };
        music: {
            prompt: string | undefined;
            provider: string | undefined;
            path: string | undefined;
            sourcePath: string | undefined;
            durationSec: number | undefined;
            status: PrimitiveStatus;
            jobId: string | undefined;
            error: string | undefined;
            cachePath: string | undefined;
            cacheKey: string | undefined;
            metadataPath: string | undefined;
            freshness: AssetFreshness | undefined;
        };
        composition: {
            path: string;
            exists: boolean;
            status: BuildReportCompositionStatus;
            cacheKey?: string;
        };
        narrationPath: string | undefined;
        narrationDurationSec: number | undefined;
        backdropPath: string | undefined;
        videoPath: string | undefined;
        musicPath: string | undefined;
        compositionPath: string;
        narrationStatus: PrimitiveStatus;
        backdropStatus: PrimitiveStatus;
        videoStatus: PrimitiveStatus;
        videoJobId: string | undefined;
        musicStatus: PrimitiveStatus;
        musicJobId: string | undefined;
        narrationError: string | undefined;
        backdropError: string | undefined;
        videoError: string | undefined;
        musicError: string | undefined;
    }[];
    beatSummary: BuildBeatSummary;
    jobs: {
        id: string;
        jobType: import("./status-jobs.js").JobType;
        status: import("./status-jobs.js").JobStatus;
        provider: string;
        providerTaskId: string;
        providerTaskType: "text2video" | "image2video" | undefined;
        beatId: string | undefined;
        outputPath: string | undefined;
        cachePath: string | undefined;
        cacheKey: string | undefined;
        canonicalPath: string | undefined;
        metadataPath: string | undefined;
        retryWith: string[];
    }[];
    outputPath: string | undefined;
    sceneRepair: BuildSceneRepairSummary;
    stageReports: Record<"assets" | "compose" | "sync" | "render", StageReport> | undefined;
    warnings: string[];
    retryWith: string[];
    totalLatencyMs: number;
};
type BuildReportCompositionStatus = "generated" | "cached" | "exists" | "needs-author" | "skipped" | "failed";
