/**
 * Frame-accurate sequence timeline model — the product-agnostic spine of the
 * sequences surface. A sequence is a fixed-fps, fixed-duration timeline of
 * tracks; clips sit on tracks at integer frame positions with non-destructive
 * source in/out points. Products bind this model to their own storage through
 * `SequenceStore` (./store) and surface it to agents through the MCP toolset
 * (./mcp).
 *
 * All positions and durations are integer FRAMES at the sequence's fps.
 * Seconds appear only at the API edge (agent tools speak seconds; the
 * dispatcher converts exactly once). Nothing here touches a database, the DOM,
 * or React.
 */
declare const MIN_SEQUENCE_CLIP_FRAMES = 1;
/** Track kinds. `reference` holds non-rendered guide media; `agent` holds the
 *  agent-decision lane rendered as markers, never as media. */
type SequenceTrackKind = 'video' | 'audio' | 'caption' | 'reference' | 'agent';
type SequenceStatus = 'draft' | 'active' | 'exporting' | 'archived';
type SequenceExportFormat = 'mp4' | 'otio' | 'xml' | 'edl' | 'vtt' | 'srt' | 'contact_sheet';
type SequenceExportStatus = 'queued' | 'processing' | 'completed' | 'failed' | 'cancelled';
type SequenceMediaKind = 'video' | 'image' | 'audio';
interface SequenceMeta {
    id: string;
    title: string;
    fps: number;
    width: number;
    height: number;
    aspectRatio: string;
    durationFrames: number;
    status: SequenceStatus;
    metadata: Record<string, unknown>;
}
interface SequenceTrack {
    id: string;
    kind: SequenceTrackKind;
    name: string;
    sortOrder: number;
    locked: boolean;
    muted: boolean;
    metadata: Record<string, unknown>;
}
/** Resolved playable media behind a clip. The store resolves product-specific
 *  references (generation rows, asset rows) into this shape; the core model
 *  never sees the product's tables. */
interface SequenceClipMedia {
    url: string;
    kind: SequenceMediaKind;
    /** Natural duration of the source media when known. */
    durationSeconds?: number;
    /** Provider job state for media still rendering upstream. */
    providerStatus?: 'queued' | 'processing' | 'completed' | 'failed';
}
interface SequenceClip {
    id: string;
    trackId: string;
    label: string;
    startFrame: number;
    durationFrames: number;
    /** Source-relative in point (frames into the source media). */
    sourceInFrame: number;
    /** Source-relative out point; null = natural end of the source. */
    sourceOutFrame: number | null;
    disabled: boolean;
    /** Caption/text body for clips on caption tracks. */
    text?: string;
    /** BCP-47 language tag for caption clips (e.g. 'en', 'es', 'ja'). */
    language?: string;
    /** Opaque product reference to a generation row, when the clip came from one. */
    generationId?: string;
    /** Opaque product reference to an asset row, when the clip came from one. */
    assetId?: string;
    media?: SequenceClipMedia;
    metadata: Record<string, unknown>;
}
/** One entry in the sequence's decision log — human edits, agent proposals,
 *  agent edits, exports, and notes all land here so the edit history is a
 *  single auditable lane. */
interface SequenceDecision {
    id: string;
    clipId: string | null;
    kind: 'human_edit' | 'agent_proposal' | 'agent_edit' | 'export' | 'note';
    instruction: string;
    reasoningSummary: string | null;
    accepted: boolean | null;
    metadata: Record<string, unknown>;
    createdAt: Date;
}
interface SequenceExportRecord {
    id: string;
    format: SequenceExportFormat;
    status: SequenceExportStatus;
    resultUrl: string | null;
    metadata: Record<string, unknown>;
    createdAt: Date;
}
/** The full timeline aggregate — what `get_timeline_state` returns and what
 *  every operation validates against. */
interface SequenceTimeline {
    sequence: SequenceMeta;
    tracks: SequenceTrack[];
    clips: SequenceClip[];
}
/** What is on screen/audible at a single frame — the answer shape for
 *  "what is happening at 0:34". */
interface SequenceFrameSnapshot {
    frame: number;
    seconds: number;
    /** Active (enabled, in-range) clips at this frame, with their track. */
    active: Array<{
        track: SequenceTrack;
        clip: SequenceClip;
    }>;
    /** Caption text visible at this frame, in track sort order. */
    captions: Array<{
        text: string;
        language?: string;
        clipId: string;
    }>;
}
declare function secondsToFrames(seconds: number, fps: number): number;
declare function framesToSeconds(frames: number, fps: number): number;
declare function formatSeconds(seconds: number): string;
/** `m:ss.ff` timecode for UI and agent-readable frame references. */
declare function formatTimecode(frames: number, fps: number): string;
interface TimelineClipBounds {
    startFrame: number;
    durationFrames: number;
}
interface TimelineInterval {
    startFrame: number;
    endFrame: number;
}
declare function clampClipStart(input: {
    startFrame: number;
    durationFrames: number;
    sequenceDurationFrames: number;
}): number;
declare function clampClipDuration(input: {
    startFrame: number;
    durationFrames: number;
    sequenceDurationFrames: number;
}): number;
declare function assertClipFitsSequence(input: {
    startFrame: number;
    durationFrames: number;
    sequenceDurationFrames: number;
    label: string;
}): void;
/** Place a caption near the playhead inside FREE space only — the caption
 *  track never double-books. Prefers fps*3 frames, floors at fps. The gap
 *  holding (or first after) the playhead wins; with everything ahead occupied
 *  the latest earlier gap is used instead. Throws when no gap can hold the
 *  minimum — the caller must supply explicit bounds or clear space. */
declare function chooseCaptionPlacement(input: {
    playheadFrame: number;
    fps: number;
    sequenceDurationFrames: number;
    occupiedIntervals: TimelineInterval[];
}): TimelineClipBounds;
/** Resolve everything active at one frame — the core of `get_frame_at_time`. */
declare function snapshotFrame(timeline: SequenceTimeline, frame: number): SequenceFrameSnapshot;
/** Occupied intervals on one track, for placement collision checks. */
declare function trackIntervals(timeline: SequenceTimeline, trackId: string): TimelineInterval[];

/**
 * Storage contract binding the sequence model to a product's database. The
 * product constructs one store per (workspace, sequence, actor) request scope —
 * RBAC and workspace isolation happen BEFORE construction (the product's
 * `requireWorkspaceAccess` equivalent); the store never re-checks identity.
 *
 * Every method throws on failure — no silent nulls, no `{ ok: false }` wrappers
 * at this layer. The MCP dispatcher (./mcp) is the boundary that converts
 * thrown errors into structured tool errors the model can read and react to.
 *
 * Mutations append to the decision log themselves only when the operation
 * dispatcher asks (`recordDecision`); plain CRUD stays log-free so human edits
 * driven by the UI can batch their own decision entries.
 */

interface NewSequenceTrack {
    kind: SequenceTrackKind;
    name: string;
    sortOrder?: number;
}
interface NewSequenceClip {
    trackId: string;
    label: string;
    startFrame: number;
    durationFrames: number;
    sourceInFrame?: number;
    sourceOutFrame?: number | null;
    text?: string;
    language?: string;
    generationId?: string;
    assetId?: string;
    metadata?: Record<string, unknown>;
}
interface SequenceClipPatch {
    trackId?: string;
    label?: string;
    startFrame?: number;
    durationFrames?: number;
    sourceInFrame?: number;
    sourceOutFrame?: number | null;
    disabled?: boolean;
    text?: string;
    language?: string;
    metadata?: Record<string, unknown>;
}
interface NewSequenceDecision {
    clipId?: string | null;
    kind: SequenceDecision['kind'];
    instruction: string;
    reasoningSummary?: string | null;
    accepted?: boolean | null;
    metadata?: Record<string, unknown>;
}
interface SequenceStore {
    /** Full aggregate: sequence meta + tracks + clips with resolved media. */
    getTimeline(): Promise<SequenceTimeline>;
    getClip(clipId: string): Promise<SequenceClip>;
    createTrack(input: NewSequenceTrack): Promise<SequenceTrack>;
    createClip(input: NewSequenceClip): Promise<SequenceClip>;
    updateClip(clipId: string, patch: SequenceClipPatch): Promise<SequenceClip>;
    deleteClip(clipId: string): Promise<void>;
    /** Grow (or shrink, never below the last clip end) the sequence duration. */
    updateSequenceDuration(durationFrames: number): Promise<SequenceMeta>;
    recordDecision(input: NewSequenceDecision): Promise<SequenceDecision>;
    createExport(format: SequenceExportFormat, metadata?: Record<string, unknown>): Promise<SequenceExportRecord>;
    listDecisions(limit?: number): Promise<SequenceDecision[]>;
    listExports(limit?: number): Promise<SequenceExportRecord[]>;
}
/** Per-request scope a product binds when constructing its store. Carried so
 *  decision rows and export rows attribute to the acting user; never trusted
 *  from tool arguments. */
interface SequenceStoreScope {
    workspaceId: string;
    sequenceId: string;
    userId: string;
}

export { snapshotFrame as A, trackIntervals as B, MIN_SEQUENCE_CLIP_FRAMES as M, type NewSequenceClip as N, type SequenceClip as S, type TimelineClipBounds as T, type NewSequenceDecision as a, type NewSequenceTrack as b, type SequenceClipMedia as c, type SequenceClipPatch as d, type SequenceDecision as e, type SequenceExportFormat as f, type SequenceExportRecord as g, type SequenceExportStatus as h, type SequenceFrameSnapshot as i, type SequenceMediaKind as j, type SequenceMeta as k, type SequenceStatus as l, type SequenceStore as m, type SequenceStoreScope as n, type SequenceTimeline as o, type SequenceTrack as p, type SequenceTrackKind as q, type TimelineInterval as r, assertClipFitsSequence as s, chooseCaptionPlacement as t, clampClipDuration as u, clampClipStart as v, formatSeconds as w, formatTimecode as x, framesToSeconds as y, secondsToFrames as z };
