import { readSceneSelectionArtifact } from './scene-selection-store.js'; import { readSceneCandidatesArtifact } from './scene-candidate-store.js'; import { refreshExecutionStatus } from './execution-status.js'; import type { VideoExecutionReport, VideoProductionMode } from './types.js'; export type AutoChainSceneRunner = (sceneIndex: number) => Promise<{ report: VideoExecutionReport; reportPath: string; }>; export interface AutoChainOptions { root: string; productionMode?: VideoProductionMode; /** Ordered subset of scene indices. When omitted, read from the storyboard. */ sceneIndices?: number[]; /** Injectable per-scene runner (tests). Defaults to a single-scene executeProject call. */ runScene?: AutoChainSceneRunner; /** * Opt-in fallback ladder. When a chained scene produces no usable candidate * (e.g. the provider rejected its video reference — Seedance RejectFace), * retry it down the ladder: chain-from-prev → chain-from-anchor → image-only, * instead of fail-fast stopping. Default off → byte-identical fail-fast. */ chainFallback?: boolean; } export interface AutoChainSceneResult { sceneIndex: number; chainedFrom: number | null; selectedCandidateId: string | null; status: 'completed' | 'failed'; /** Set when the fallback ladder used a non-default chain source (e.g. 'chain-from-0', 'image-only'). */ fallback?: string; } export interface AutoChainReport { schemaVersion: 1; projectSlug: string; mode: 'auto-chain'; scenes: AutoChainSceneResult[]; stoppedAt: number | null; /** * Continuation advisories (chain-depth drift). Present only when non-empty — * additive, so shallow chains keep the current report shape. */ advisories?: string[]; } /** * Poll a just-submitted scene to completion. On async routes (e.g. * seedance-direct) `executeProject` returns after SUBMIT — the render finishes * later via polling — so auto-chain MUST wait for the scene's candidate to gain * a video output before the next scene can chain from it (otherwise the chain * seed resolves to a video-less candidate and `chain-from-prev-source-missing` * fires). Returns true once a video output exists; false if the job fails or the * attempts are exhausted. Dependencies are injectable for offline tests. */ export declare function waitForSceneVideo(projectSlug: string, sceneIndex: number, deps: { root: string; productionMode?: VideoProductionMode; refresh?: typeof refreshExecutionStatus; readCandidates?: typeof readSceneCandidatesArtifact; sleep?: (ms: number) => Promise; intervalMs?: number; maxAttempts?: number; }): Promise; /** * Adopt-in-flight guard (shared by all three unattended drivers). Returns the id * of a candidate that was ALREADY submitted for this scene on a prior/concurrent * pass — a candidate carrying a live provider job id (`source.externalJobId`), * not `failed`, for a scene with no selection yet — or null. * * The drivers' resume check is selection-only, so a scene that crashed AFTER the * provider submit but BEFORE `selectCandidate` (a poll window that can be many * minutes on async routes) reads as "not done" and gets RE-SUBMITTED → a second * paid render for the same scene. When this returns a candidate, the caller must * poll+adopt it via {@link resolveInFlightScene} instead of submitting again. * Prefers the highest generationRound (the most recent attempt). */ export declare function findInFlightCandidateId(root: string, projectSlug: string, sceneIndex: number, deps?: { readCandidates?: typeof readSceneCandidatesArtifact; readSelection?: typeof readSceneSelectionArtifact; }): Promise; /** * Poll an in-flight scene (found via {@link findInFlightCandidateId}) to a * TRI-STATE outcome — deliberately NOT a boolean, because the distinction is * spend-critical: * - `'rendered'`: a video output landed → the caller selects the adopted * candidate (the common crash-during-poll case: the render finished server- * side while the operator was away). * - `'failed'`: the provider reported the job failed → the prior spend is * definitively resolved, so a fresh submit is safe (NOT a double charge). * - `'pending'`: still running at the provider after the poll budget → the * caller must NOT re-submit (that WOULD double-charge); re-running later * re-adopts it. Conflating this with 'failed' is what would reintroduce the * double-submit, so they are kept distinct. */ export declare function resolveInFlightScene(projectSlug: string, sceneIndex: number, deps: { root: string; productionMode?: VideoProductionMode; refresh?: typeof refreshExecutionStatus; readCandidates?: typeof readSceneCandidatesArtifact; sleep?: (ms: number) => Promise; intervalMs?: number; maxAttempts?: number; }): Promise<'rendered' | 'failed' | 'pending'>; /** * Drive the existing chain-from-prev engine across a whole storyboard, * unattended. Renders scenes sequentially; each scene after the first seeds * from the previous scene's auto-selected output video (chainFromPrev) with * continuity prompt-augmentation bundled in. Resumable (skips scenes that * already have a selection) and fail-fast (halts at the first scene that yields * no usable candidate, recording stoppedAt). */ export declare function runAutoChain(projectSlug: string, options: AutoChainOptions): Promise; //# sourceMappingURL=execute-autochain.d.ts.map