import type { ChildProcess } from 'child_process';
import { AgentVariantsError } from './errors';
import { SessionStore, type ApprovedBriefSelection, type ApproveResult, type ProposeArgs, type ProposeResult, type ReportCompleteResult, type RequestWorkResult } from './SessionStore';
import type { ActiveProjectContext, ActiveVariantSnapshot, Brief, DesignContextEntry, ProjectContext, SourceContextInput, Stage, Target, TerminalSummary, VariantQaResult, VariantSessionArtifact, WorkItemStatus } from './contracts';
import type { PendingChangesAdapter } from './pendingChangesAdapter';
import { VariantHistoryService } from '../../services/VariantHistoryService';
/**
 * Subset of WorktreeManager that the orchestrator depends on. Defined as an
 * interface so unit tests can mock it without spinning up real git/processes.
 *
 * Each succeeded variant gets its own dev server so the user can cycle
 * between live, running variants in the iframe via a proxy retarget.
 */
export interface WorktreeManagerLike {
    createWorktrees(sessionId: string, count: number): Promise<string[]>;
    getDiff(worktreePath: string): Promise<string>;
    /** Apply a unified diff to a project working tree (no commit). Used by
     *  commitVariant to land the chosen variant on the user's branch
     *  immediately rather than waiting for an agent to pick up pending-changes. */
    applyDiffToProject?(diff: string, projectPath: string): Promise<void>;
    /** `freshDestinationParent` is required when the session was fresh — it
     *  tells WorktreeManager which `<destinationParent>/.rivet-variants/` root
     *  the worktrees live under (since fresh worktrees are no longer in /tmp). */
    cleanupSession(sessionId: string, freshDestinationParent?: string): Promise<void>;
    getFreePort(): Promise<number>;
    startDevServer(worktreePath: string, port: number, cmd: string, args: string[], env: Record<string, string>): Promise<ChildProcess>;
    stopDevServer(proc: ChildProcess, timeoutMs?: number): Promise<void>;
    getProjectCwdInWorktree(worktreePath: string): Promise<string>;
    /** Optional fresh-project scaffold support — present in production but
     *  optional so unit tests can mock with the minimum surface they need.
     *  `destinationParent` is the user-supplied workspace parent directory;
     *  fresh worktrees are scaffolded under `<destinationParent>/.rivet-variants/`
     *  so commit time can be a same-volume rename. */
    createFreshWorktrees?(sessionId: string, count: number, template: import('../../services/templates/viteReactTs').TemplateBundle, designContext: ReadonlyArray<DesignContextEntry | undefined> | undefined, sourceContext: SourceContextInput | undefined, destinationParent: string): Promise<string[]>;
    /** Move a fresh-worktree directory to its history location, preserving
     *  contents. Optional on the interface so unit tests can stub partially. */
    moveWorktreeToHistory?(srcWorktreePath: string, historyDir: string, folderName: string): string;
    /** Repoint a worktree's node_modules to a shared install via relative
     *  symlink. Optional on the interface so unit tests can stub partially. */
    repointNodeModulesSymlink?(worktreePath: string, sharedNodeModulesAbsPath: string): void;
}
/**
 * Per-session resolved environment. Used to populate destinationPath on the
 * variant envelope and (in fresh-project flows) the workspace target. Also
 * feeds buildDevServerCommand so each variant's dev server gets the correct
 * port flag for its framework.
 */
export interface ProjectEnvironment {
    projectPath: string;
    framework: string;
    packageManager: string;
    devCommand: string;
    /** Builds (cmd, args, env) for spawning a dev server in a worktree on a
     *  specific port. Defaults to a generic { packageManager, [devCommand,
     *  '--port', port] } shape if not provided, but production wires this to
     *  buildDevServerCommand from utils/devServerCommand.ts. */
    buildDevCommand?: (port: number) => {
        cmd: string;
        args: string[];
        env: Record<string, string>;
    };
}
/**
 * Runs preview QA against a static_preview variant before the orchestrator
 * marks it as succeeded. Default implementation in
 * `defaultPreviewQaRunner` performs static asset reachability checks via
 * the global `fetch`. Tests inject a deterministic stub so they can
 * exercise QA-pass, asset-failure, and runtime/console failure paths
 * without network or browser dependencies.
 */
export type PreviewQaRunner = (args: {
    sessionId: string;
    workItemId: string;
    html: string;
}) => Promise<VariantQaResult>;
export interface OrchestratorDeps {
    store: SessionStore;
    worktreeManager: WorktreeManagerLike;
    pendingChangesAdapter: PendingChangesAdapter;
    resolveProjectEnvironment: (sessionId: string) => Promise<ProjectEnvironment>;
    telemetry?: VariantsTelemetry;
    /** Background `npm install` runner for fresh worktrees. Defaults to a real
     *  spawn-based runner; tests inject a fast no-op or rejecting stub. */
    installDependencies?: (worktreePath: string) => Promise<void>;
    /** Server-side materialize step for fresh-project commits. Copies the
     *  source worktree to `destinationPath` excluding install artifacts and
     *  initializes a fresh git repo. Defaults to a real fs.cp + simpleGit
     *  implementation; tests inject a stub. */
    materializeProject?: (sourceWorktreePath: string, destinationPath: string) => Promise<void>;
    /** Override the preview QA runner. See `PreviewQaRunner` for the contract. */
    previewQaRunner?: PreviewQaRunner;
    /** Retarget the iframe proxy to a different dev-server port (or back to the
     *  default upstream when `null`). Used by `commitVariant` on the fresh
     *  vite_app rename path: after the chosen variant is renamed into
     *  destinationPath, the orchestrator spawns a fresh dev server there and
     *  retargets so the iframe stays live instead of flashing "preview
     *  disconnected." Optional — tests can omit, and production wires it to
     *  `SessionBridgeService.switchPreviewPort`. */
    switchPreviewPort?: (port: number | null) => void;
    /** Advertise a Rivet-spawned committed dev/static server on the bridge's
     *  `devServerHealth` so `/api/mcp/status` and the iframe's upstream-probe
     *  see `ownership: 'rivet_owned'`. Distinct from `switchPreviewPort`
     *  (which is also called during variant cycling and on cancel — those
     *  must NOT mutate the user's external dev-server health). Only the
     *  commit-handoff path calls this. Production wires it to
     *  `SessionBridgeService.setCommittedDevServerHealth`. */
    setCommittedDevServerHealth?: (port: number | null) => void;
    /** Local persistence for the variant-history feature. Writes every
     *  completed code_gen variant (and committed/rejected fresh-project
     *  variants) into `<projectPath>/.rivet/variants/` so the desktop UI and
     *  MCP `list_past_variants` tool can surface prior runs. Optional in the
     *  constructor for tests; production wires a real instance. */
    variantHistory?: VariantHistoryService;
    /** Spawn a static file server bound to a committed `static_preview`
     *  project. Defaults to the real `startStaticPreviewServer` (spawns a Node
     *  child process). Tests inject a fake that returns a stub `ChildProcess`
     *  so they don't bind real ports. */
    startStaticPreviewServer?: (args: {
        rootPath: string;
        port: number;
        host?: string;
    }) => Promise<ChildProcess>;
    /** Hard ceiling (ms) on worktree provisioning before the kickoff fails fast.
     *  Defaults to `RIVET_VARIANTS_PROVISION_TIMEOUT_MS` / 180s. Tests inject a
     *  tiny value to exercise the timeout path without real slow I/O. */
    provisionTimeoutMs?: number;
}
/**
 * Minimal telemetry surface the orchestrator depends on. Decoupled from
 * TelemetryService so unit tests can pass a stub. Production wires this
 * to the singleton TelemetryService instance.
 */
export interface VariantsTelemetry {
    track(event: string, properties?: Record<string, unknown>): void;
    trackAgentVariantsScaffoldStarted?: (data: {
        sessionId: string;
        variantCount: number;
        designContext?: Array<{
            slot: number;
            kind: 'slug' | 'markdown';
            slug?: string;
            markdownLabel?: string;
        }> | null;
    }) => void;
    trackAgentVariantsScaffoldCompleted?: (data: {
        sessionId: string;
        durationMs: number;
        installDurationMs: number;
        variantCount: number;
    }) => void;
    trackAgentVariantsScaffoldFailed?: (data: {
        sessionId: string;
        errorCode: string;
        durationMs: number;
    }) => void;
    trackAgentVariantsFreshDevServerStarted?: (data: {
        sessionId: string;
        variantId: string;
        port: number;
    }) => void;
    trackAgentVariantsFreshDevServerFailed?: (data: {
        sessionId: string;
        variantId: string;
        errorCode: string;
    }) => void;
    trackAgentVariantsNewProjectMaterialized?: (data: {
        sessionId: string;
        variantId: string;
        destinationPath: string;
        changedFilesCount: number;
        durationMs: number;
    }) => void;
    trackAgentVariantsSourceContextQuality?: (data: {
        sessionId: string;
        sourceUrlCount: number;
        artifactCount: number;
        hasScreenshotReferences: boolean;
        preserveBrand: boolean;
    }) => void;
    trackAgentVariantsBriefQuality?: (data: {
        sessionId: string;
        briefCount: number;
        evidenceBackedCount: number;
        shortBriefCount: number;
    }) => void;
    trackAgentVariantsStaticPreviewCompleted?: (data: {
        sessionId: string;
        variantId: string;
        durationMs: number | null;
    }) => void;
}
/** Snapshot shape consumed by the iframe chip, both via SSE and the legacy
 *  GET /api/variants/active endpoint. Single source of truth lives on the
 *  orchestrator (`buildActiveSnapshot`) so both transports are consistent. */
export type ActiveSnapshot = {
    active: false;
} | {
    active: true;
    sessionId: string;
    stage: Stage;
    briefs: Brief[];
    progress: {
        ready: number;
        total: number;
    };
    summary: TerminalSummary | null;
    variants: ActiveVariantSnapshot[];
    /** Project context for the session — surfaced so the chip can render
     *  a "Creating new project at {destinationPath}" header for fresh
     *  sessions vs the default refinement header. */
    projectContext: ActiveProjectContext;
    /** For fresh sessions, the user-facing destination path the materialize
     *  step will land at. Undefined for existing-project sessions. */
    destinationPath?: string;
    /** Supporting artifacts the agent used or generated for this session
     *  (e.g. the DESIGN.md inspirations for a 0→1 run). Surfaced under the
     *  variants list so the user can inspect the hidden MCP context. */
    artifacts?: VariantSessionArtifact[];
};
/**
 * Wraps SessionStore for the operations that have side effects: approve
 * (provision worktrees), reportComplete (capture diff + auto-enqueue to
 * pending changes), cancel (teardown). Pure read paths bypass and call
 * SessionStore directly.
 *
 * This is the terminal-native variant of the orchestrator: the user picks
 * a single brief upfront, so there's only ever one code-gen item per
 * session and no live preview / proxy retarget. Once the agent reports
 * the variant succeeded, the diff auto-enqueues to bridge — no separate
 * pick step.
 */
export declare class AgentVariantsOrchestrator {
    private readonly store;
    private readonly worktrees;
    private readonly adapter;
    private readonly resolveEnv;
    private readonly telemetry;
    private readonly installDependencies;
    private readonly materializeProject;
    private readonly previewQaRunner;
    private readonly switchPreviewPort;
    private readonly setCommittedDevServerHealth;
    private readonly variantHistory;
    private readonly startStaticPreviewServerImpl;
    private readonly provisionTimeoutMs;
    private readonly resources;
    /**
     * Committed dev servers from prior sessions that survived teardown. The
     * orchestrator outlives any single session — once a new session starts, the
     * prior committed dev server is no longer needed (the next session opens
     * its own preview chip), so stop it before the new session's first preview
     * goes live. Keyed by sessionId for diagnostics; values are kept alive
     * intentionally between commit and next `propose` / `shutdown`.
     */
    private readonly lingeringCommittedDevServers;
    /**
     * Per-session in-flight commit chain. Two overlapping commitVariant calls
     * would otherwise both pass the `getVariantPick` idempotency check (the
     * pick isn't recorded until after `applyDiffToProject`) and double-invoke
     * `git apply` — which corrupts the working tree on the second run. The
     * chain serializes commits per session so the second caller waits for the
     * first to finish and then hits the idempotency check (returning
     * `duplicate: true` for the same variantId, or `PENDING_CHANGE_CONFLICT`
     * for a different one).
     */
    private readonly inflightCommits;
    /** Most recent agent-variants sessionId — read by the iframe chip via
     *  GET /api/variants/active. Cleared on cancel or commit. */
    private activeSessionId;
    /** Push channel for the iframe chip's SSE subscription. Emits an
     *  ActiveSnapshot on every state change so the chip never has to poll. */
    private readonly events;
    constructor(deps: OrchestratorDeps);
    propose(args: ProposeArgs): ProposeResult;
    /** Returns the most recent agent-variants sessionId (or null). */
    getActiveSessionId(): string | null;
    /** Subscribe to active-snapshot pushes. Returns an unsubscribe fn. */
    subscribeToEvents(listener: (snapshot: ActiveSnapshot) => void): () => void;
    /** Number of live Rivet UI clients subscribed to the variants SSE stream.
     *  A subscribed client means a Rivet page is open in the browser. */
    private uiClientCount;
    /** Wall-clock of the last UI-client connect/disconnect. `hasUiClients`
     *  treats a very recent disconnect as still-connected to ride out the brief
     *  gap when an EventSource drops and auto-reconnects (default retry ~3s) —
     *  otherwise a `propose`/`start` landing in that window reads zero clients
     *  and spawns a duplicate browser tab. */
    private lastUiClientActivityAt;
    /** Grace window covering an EventSource reconnect (its default retry is ~3s;
     *  the SSE heartbeat is 30s). Long enough to span a reconnect, short enough
     *  that a genuine page close frees the "reopen on next propose" path quickly. */
    private static readonly UI_CLIENT_GRACE_MS;
    /**
     * Register a live Rivet UI client (called when the variants SSE stream opens).
     * Returns a release fn to call on disconnect. Used as the "a Rivet page is
     * already open" signal so the MCP server can avoid spawning a duplicate tab
     * every time the variants flow re-enters an already-active editor session.
     */
    registerUiClient(): () => void;
    /** True when at least one Rivet UI page is currently connected — or one
     *  disconnected within the grace window (an EventSource mid-reconnect). */
    hasUiClients(): boolean;
    /** Build the snapshot the chip cares about. Reads from SessionStore +
     *  per-resource state; safe to call at any time. */
    buildActiveSnapshot(): ActiveSnapshot;
    private emitChange;
    reportBriefs(args: {
        sessionId: string;
        workItemId: string;
        attempt: number;
        leaseId?: string;
        briefs: Brief[];
    }): {
        briefs: Brief[];
    };
    reportSourcePlan(args: import('./SessionStore').ReportSourcePlanArgs): {
        briefWorkItem: import('./contracts').WorkItem;
    };
    requestWork(args: {
        sessionId: string;
        leaseOwner: string;
        requestedLeaseCount?: number;
        workItemId?: string;
    }): RequestWorkResult;
    beginStaticPreviewRefinements(args: {
        sessionId: string;
        targets: Array<{
            variantId: string;
            instruction: string;
        }>;
    }): void;
    getVariantPick(sessionId: string): ReturnType<SessionStore['getVariantPick']>;
    refineVariant(args: {
        sessionId: string;
        variantId: string;
        instruction: string;
        leaseOwner: string;
    }): RequestWorkResult;
    getStage(sessionId: string): Stage;
    getBriefs(sessionId: string): Brief[];
    getProgress(sessionId: string): {
        ready: number;
        total: number;
    };
    getSummary(sessionId: string): TerminalSummary;
    getVariants(sessionId: string): ActiveVariantSnapshot[];
    hasSession(sessionId: string): boolean;
    /** Read the projectContext for a session — used by Express routes that
     *  build the chip snapshot via `stateForSession`. */
    getProjectContext(sessionId: string): ProjectContext;
    /** Resolve a worktree path for a code-gen work item, if provisioned. */
    getWorktreePath(sessionId: string, workItemId: string): string | undefined;
    /**
     * Resolve the project cwd inside a worktree so subdirectory projects run
     * from the same repo-relative location.
     */
    getProjectCwdInWorktree(worktreePath: string): Promise<string>;
    /** Resolve the dev server port for a code-gen work item, if running. */
    getDevServerPort(sessionId: string, workItemId: string): number | undefined;
    getStaticPreviewHtml(sessionId: string, workItemId: string): string | undefined;
    /** Resolve the DESIGN.md markdown behind an artifact link. */
    getDesignContextMarkdown(sessionId: string, artifactId: string): string | undefined;
    /** Build the rendered DesignMD document for an artifact link. */
    getDesignContextViewerHtml(sessionId: string, artifactId: string): string | undefined;
    /**
     * `true` when the variant has a non-empty `assetBase` — i.e. its inlined
     * HTML references sibling files served by the asset route. The route
     * uses this to decide whether to inject a `<base href>` (only needed
     * when relative URLs in the HTML must resolve against the asset
     * sub-path). Self-contained HTML keeps its natural URL resolution.
     */
    hasStaticPreviewAssets(sessionId: string, workItemId: string): boolean;
    /**
     * Resolve an absolute, sandboxed path for a sibling asset that the
     * variant's inlined HTML references (e.g. `./avatar.glb`, `./hero.png`).
     * Returns `undefined` when the variant has no `assetBase`, the requested
     * path escapes the base (path traversal), or the file does not exist.
     *
     * Used by the `/api/variants/:sessionId/static/:variantId/<asset>` route
     * to serve assets from the agent's on-disk variant workspace pre-commit.
     */
    resolveStaticPreviewAssetPath(sessionId: string, workItemId: string, requestedPath: string): string | undefined;
    private getStaticPreviewByBriefId;
    private buildStaticPreviewUrl;
    approve(args: {
        sessionId: string;
        selections: ApprovedBriefSelection[];
    }): Promise<ApproveResult>;
    /**
     * Unified one-call kickoff that collapses propose → report_briefs →
     * approve. Synthesizes placeholder briefs server-side (the agent's
     * streamed first-line label replaces them in the UI) and immediately
     * approves so the session lands in `work_items_ready`.
     *
     * Supports both existing-project sessions (spawns code_gen work items
     * for the agent to lease) and zero-to-one sessions (spawns
     * static_preview work items; the server runs scaffold_base in the
     * background). This is the single-call path; source-grounded zero-to-one
     * sessions (source URLs / inspiration extraction) run through `propose`
     * instead so the source-research flow can gate generation — start_variants
     * routes them there. This method does not run source research.
     *
     * Host-agnostic: no LLM calls happen here. The agent (Claude Code,
     * Cursor, Codex) generates the label and code when it leases the
     * work items via continue_variants(action="request_work").
     */
    startUnified(args: {
        prompt: string;
        count?: number;
        target?: Target;
        projectContext?: ProjectContext;
        /** Agent-supplied 1–2 word label for the run's intent (heuristic fallback
         *  when absent). Shared by every variant in the run. */
        runLabel?: string;
        /** When provided, the orchestrator leases all spawned variant work
         *  items immediately so the caller can hand the lease + worktreePaths
         *  back to the agent in the same response — saving a separate
         *  continue_variants(request_work) round-trip. */
        leaseOwner?: string;
        /** Per-variant briefs supplied by the agent (label + body, optional
         *  visualReferenceUrl). When provided, briefs.length overrides any
         *  explicit count. When absent, the server stubs generic
         *  "Variant N" labels and a copy of the prompt as each body. */
        briefs?: import('./contracts').StartVariantsBriefContent[];
    }): Promise<{
        sessionId: string;
        variants: Array<{
            variantId: string;
            briefId: string;
            label: string;
            status: 'pending';
        }>;
        scaffoldBaseWorkItemId: string | null;
        /** Populated when `leaseOwner` was provided. */
        leaseId?: string;
        leaseTtlMs?: number;
        leasedWorkItems?: import('./SessionStore').RequestWorkResult['leasedWorkItems'];
    }>;
    reportComplete(args: {
        sessionId: string;
        workItemId: string;
        leaseId: string;
        attempt: number;
        status: WorkItemStatus;
        output?: unknown;
        error?: {
            code: string;
            message: string;
        };
        tokensIn?: number;
        tokensOut?: number;
        model?: string;
    }): Promise<ReportCompleteResult>;
    cancel(args: {
        sessionId: string;
        reason?: string;
    }): Promise<{
        stage: 'cancelled';
        cleanupStatus: 'pending' | 'complete' | 'partial';
    }>;
    /**
     * Cancel a single variant within a session. Siblings continue. Idempotent
     * on terminal states. Per-variant worktree teardown is deferred to a
     * follow-up; the lease is released, so requestWork won't re-surface this
     * variant and the agent's report_variant_complete for it (if it lands
     * after cancellation) is rejected by the stale-lease check.
     */
    cancelVariant(args: {
        sessionId: string;
        variantId: string;
        reason?: string;
    }): Promise<{
        sessionId: string;
        variantId: string;
        finalStatus: WorkItemStatus;
        sessionStage: Stage;
        alreadyTerminal: boolean;
    }>;
    /**
     * Remove a variant from the UI. Hides it from the active snapshot for good
     * (it's filtered out of progress/summary/terminal accounting too) while
     * retaining it in `.rivet/variants/` with status `removed` so the work is
     * kept, never rendered again. Mirrors `cancelVariant`'s shape; idempotent.
     */
    removeVariant(args: {
        sessionId: string;
        variantId: string;
    }): Promise<{
        sessionId: string;
        variantId: string;
        finalStatus: WorkItemStatus;
        sessionStage: Stage;
        alreadyRemoved: boolean;
    }>;
    /**
     * Rename a variant's user-visible title. Patches the in-memory work item so
     * the next snapshot carries the new label, persists it to the variant's
     * `.rivet/variants/` manifest (best-effort — a never-persisted variant simply
     * skips the disk write), and pushes the updated snapshot over SSE. Mirrors
     * `removeVariant`'s shape.
     */
    renameVariant(args: {
        sessionId: string;
        variantId: string;
        label: string;
    }): Promise<{
        sessionId: string;
        variantId: string;
        label: string;
        sessionStage: Stage;
    }>;
    /**
     * User has reviewed the rendered variants in chat and picked one. Look up
     * the captured diff, build a VariantPickEnvelope, and enqueue to the
     * pending-changes channel. Idempotent on (sessionId, variantId): repeating
     * the call returns duplicate=true without re-enqueueing.
     */
    commitVariant(args: {
        sessionId: string;
        variantId: string;
        skipTeardown?: boolean;
    }): Promise<{
        enqueued: boolean;
        duplicate: boolean;
        changedFilesCount: number;
        payloadKind?: 'diff' | 'diff-applied' | 'project-created';
        destinationPath?: string;
        previewUrl?: string;
    }>;
    private commitVariantImpl;
    cleanupCommittedSession(sessionId: string): Promise<void>;
    /**
     * Ensure the user-facing destination path can receive the new project.
     * Rejects when the path exists and is non-empty.
     *
     * `.rivet/` (pre-commit snapshots + history manifests) and `.gitignore`
     * (written by VariantHistoryService.ensureGitignore when variants persist
     * at success time) are both tolerated — neither is user-authored content
     * that would be clobbered by the materialize step.
     */
    private assertDestinationAvailable;
    /** Read the captured diff for a code-gen variant, if available. */
    getVariantDiff(sessionId: string, variantId: string): string | undefined;
    private provisionWorktrees;
    /**
     * Existing-project flow: clone the user's repo N times via git worktree.
     * Runs synchronously because the operation is fast (no install needed —
     * node_modules is symlinked from the source project).
     */
    private provisionExistingWorktrees;
    /**
     * Fresh-project flow: scaffold one template per variant + DESIGN.md per
     * slot synchronously (fast — just file writes + git init), then dispatch a
     * background `npm install` task per worktree. Returns immediately so the
     * `approve_variant_briefs` MCP tool doesn't time out on cold installs.
     *
     * On install success: `sessionStore.completeInternal(scaffold_base)` runs,
     * which unblocks the dependent code_gen items.
     *
     * On install failure: the scaffold_base work item is marked failed via
     * `failInternal`, which cascades DEPENDENCY_FAILED to all dependent
     * code_gen items so the session degrades cleanly.
     */
    private provisionFreshWorktrees;
    /**
     * Run `npm install` in parallel across all fresh worktrees. On all-success,
     * mark scaffold_base succeeded via `completeInternal`. On any failure,
     * mark scaffold_base failed via `failInternal` and emit telemetry.
     */
    private runBackgroundInstall;
    /**
     * Gate `succeeded` reports for `static_preview` items through preview QA
     * before they reach the store. Returns either `null` (caller continues
     * with the original args — non-static-preview report, non-succeeded
     * status, no parseable HTML, or QA passed) or an override carrying a
     * `failed` rewrite of the report. The QA result is also memoized on the
     * session resources so `getVariants` can surface it.
     *
     * Only fresh-project sessions go through this gate today; existing-
     * project `code_gen` variants produce a diff with no preview document
     * to inspect, so they skip QA and behave exactly as before.
     */
    private evaluateQaForReport;
    private handleSucceededReport;
    private handleStaticPreviewRefinement;
    /**
     * Persist a completed existing-project code_gen variant into
     * `<env.projectPath>/.rivet/variants/<sessionId>/<variantId>/`. Called from
     * `handleSucceededReport` once the worktree diff has been captured but
     * before the user picks. Status is `completed` until `commitVariant` or a
     * cancellation transitions it.
     */
    private persistCompletedExistingVariant;
    /**
     * Existing-mode `framework: 'static'` projects have no dev command, so there
     * is no per-variant dev server for the iframe to proxy. Register the variant's
     * materialized static site as a {@link StaticPreviewRecord} so the existing
     * static-file plumbing — the `/api/variants/:sid/static/:vid[/asset]` routes,
     * {@link getStaticPreviewHtml}, and {@link getVariants} (preview.kind=
     * 'static_artifact', view enabled) — all light up exactly as they do for
     * fresh `static_preview` variants. The entry HTML and its sibling assets are
     * served straight from the variant's worktree project root (session-scoped,
     * pre-commit; the persisted `.rivet/variants/.../files/` copy backs the
     * post-session history route separately).
     *
     * Returns `true` when a static preview was registered (caller skips
     * dev-server startup); `false` for non-static projects (fall back to the
     * dev-server proxy) or when no renderable entry file is present.
     */
    private registerExistingStaticPreview;
    /**
     * Persist a completed fresh-project variant into
     * `<projectContext.workspacePath>/.rivet/variants/<sessionId>/<variantId>/`.
     * Called from `handleSucceededReport` right after the per-variant snapshot
     * lands on disk — for static_preview that's the `.rivet/<slug>/` snapshot
     * dir; for vite_app fresh variants it's the worktree itself. Status starts
     * as 'completed' and transitions to 'committed' / 'rejected' / 'cancelled'
     * via `markStatus` at commit or teardown time.
     *
     * Running at success time (rather than commit time) means the history panel
     * populates live as variants generate — including when the user never picks
     * one. Mirrors the existing-project persistCompletedExistingVariant flow.
     */
    private persistCompletedFreshVariant;
    /**
     * At commit time, flip persisted variant manifests to terminal statuses
     * ('committed' for the chosen, 'rejected' for the rest). Both existing- and
     * fresh-project variants have already been persisted at code_gen success
     * time, so this is a pure status update — no source-dir copy required.
     * Also records the destination path on the chosen variant so history can
     * surface "this variant became <path>".
     */
    private persistVariantHistoryAtCommit;
    /**
     * Flip persisted variant manifests to status='cancelled' for every variant
     * in the session that was already snapshotted to disk. Fresh- and existing-
     * project variants both persist at success time now, so both need their
     * manifests flipped here. Best-effort — missing manifests are silently
     * skipped by VariantHistoryService.markStatus.
     */
    private markPersistedVariantsCancelled;
    private markPersistedVariantCancelled;
    private markPersistedVariantRemoved;
    private markPersistedVariantRenamed;
    /**
     * Resolve the project path that owns `.rivet/variants/` for a session.
     * Existing sessions: the user's project (via `resolveEnv`). Fresh sessions:
     * the *workspace root*, which is the parent of `workspacePath` — variants
     * accumulate there across sessions instead of being scattered under each
     * subproject. Must match what `persistCompletedFreshVariant` writes to.
     */
    private resolveHistoryProjectPath;
    private readManifest;
    /**
     * Resolve dev server invocation for a worktree. Fresh-project worktrees
     * always use the Vite template's npm command at the worktree root; existing
     * projects defer to the user's framework/packageManager config.
     */
    private resolveDevServer;
    /**
     * After the chosen variant has been renamed into `destinationPath`, move
     * each unchosen sibling worktree into a `<destinationParent>/<slug>-variants/NN-<label>/`
     * folder and repoint its `node_modules` symlink at the chosen project's
     * real install. Writes a manifest documenting all 5 variants (the chosen
     * one with `folder: null` and `chosenPath: '../<slug>'`).
     *
     * Sets `vitePreservedSiblings = true` on session resources so the
     * subsequent teardown skips `cleanupFreshWorktrees` (which would otherwise
     * delete the just-preserved history). Dev servers still get stopped.
     */
    private preserveUnchosenVariants;
    /**
     * Rename `sourceWorktreePath` into `destinationPath`, then replace the
     * worktree's `.git` with a fresh-history init + initial commit. Matches
     * the historical materializeProject behavior of "user sees a fresh repo
     * at destinationPath" so we don't surprise them with a `rivet: variant
     * baseline` commit they didn't intend.
     *
     * The rename is the load-bearing operation: it moves the entire variant
     * tree (template files + materialized node_modules) in O(1) when the
     * source and destination share a volume. The git reset that follows is
     * best-effort housekeeping; a failure there leaves files in place.
     */
    private renameWorktreeIntoDestination;
    /**
     * Stop every variant dev server currently running for `resources`. Clears
     * the `devServerProcess` / `port` fields on each record so subsequent
     * teardown logic doesn't try to stop them a second time. Called from
     * `commitVariant` (vite_app rename path) BEFORE the worktree directory is
     * renamed — leaving Vite alive across the rename leaks empty `.vite/`
     * stub dirs at the old paths.
     */
    private stopAllDevServersForSession;
    /**
     * Spawn a fresh dev server at the user-facing destinationPath and retarget
     * the proxy so the iframe stays live across commit. Stores the process on
     * `resources.committedDevServer` so teardown can hand it off to the
     * lingering registry instead of killing it. Best-effort: a failure here is
     * non-fatal — the commit still succeeds; the user just has to run
     * `npm run dev` themselves to bring the preview back.
     *
     * `mode` selects the server flavor: `vite_app` spawns Vite via `npm run
     * dev`; `static_preview` spawns the in-tree static file server bound to
     * the destination directory (no install, no build, ~ms startup).
     */
    private startCommittedDevServer;
    /**
     * Stop any committed dev servers that were left alive past their session's
     * teardown. Called at the start of a new `propose` (so the next session's
     * preview doesn't compete with stale processes) and exposed for explicit
     * shutdown hooks. Best-effort: per-server failures are logged but don't
     * propagate.
     */
    private stopLingeringCommittedDevServers;
    private teardownSession;
    private ensureResources;
}
export declare function buildStaticPreviewDocument(input: {
    html: string;
    css?: string;
    js?: string;
}): string;
export type { AgentVariantsError };
//# sourceMappingURL=WorktreeOrchestrator.d.ts.map