/**
 * Asset index sweep (asset-as-embed) — the boot/recovery trigger of the image
 * caption / OCR pass.
 *
 * OV1: this is SEPARATE from the text/FTS reindex. The vector reindex (boot /
 * git-pull / reconcile) only refreshes the FTS+embedding text for changed
 * entries; it deliberately does NOT fire vision calls. This sweep is the one
 * place that drives image captioning, so vision spend is intentional and
 * bounded, never a side effect of a routine reindex.
 *
 * Asset-as-embed: derivation is keyed by the immutable assetId (the assets.db
 * row), NOT an `asset` entity — that type was retired by Phase F. Per indexable
 * IMAGE row (assets.db `media_type === 'image'`, non-SVG):
 *  - PERF1: reads the unfinished set from the LanceDB index-state table (one
 *    query) and skips rows already `full`/`failed` at the current version.
 *  - Migration (Codex CRITICAL): a row with NO state, or a row at a STALE
 *    `index_version`, is RE-derived — undefined is never treated as "done".
 *    `failed` at the current version is terminal (don't re-bill every boot).
 *  - Caption + OCR (bounded fan-out, A3). When captioning is unreachable
 *    (gateway / disabled / non-vision model) the image is left honestly
 *    `pending`, so flipping the gate or model later picks it up.
 *  - SVG is skipped — transitional until sanitize-html is wired.
 *
 * On a successful caption the text is stored in `assets.db.caption` (decision
 * D1 — DB column, NO sidecar; survives deletion of any embedding entry) and the
 * entries that EMBED the asset are re-upserted so the caption folds into their
 * search text (decision D2). The expensive work (vision, byte downloads) runs
 * CONCURRENTLY (bounded); the LanceDB index-state + embedder writes run SERIALLY
 * through `vectorService` (optimistic concurrency — racing mergeInserts conflict).
 */
import { type CaptionModelConfig, type CaptionCompleteFn } from './asset-caption-service.js';
import { type VectorService } from './vector-service.js';
import type { AssetIndexService } from './assets/asset-index-service.js';
import type { EntityFile } from '../core/graph.js';
/** Structural slice of a graph the sweep needs (avoids an import cycle). */
interface SweepGraph {
    /** All entities in the repo — scanned once per sweep to find the entries
     *  that embed a freshly-captioned asset, for re-index. */
    list(): EntityFile[];
    /** Download an immutable-keyed asset's bytes by id + filename. */
    downloadAssetById(assetId: string, filename: string): Promise<Buffer>;
}
/** Structural slice of the workspace manager the sweep needs. */
export interface SweepWorkspaceManager {
    vectorService?: VectorService;
    getGraph(repoName: string): SweepGraph;
    getAssetIndex(): AssetIndexService;
}
export interface AssetIndexSweepDeps {
    workspaceManager: SweepWorkspaceManager;
    repos: Array<{
        name: string;
        path?: string;
    }>;
    workspacePath: string;
    /** Resolved inference coordinates for captioning; null when none configured. */
    captionConfig: CaptionModelConfig | null;
    /** Per-workspace gate (default-on). When false, images are left pending. */
    captioningEnabled: boolean;
}
export interface AssetIndexSweepOptions {
    log?: (msg: string) => void;
    /** Bounded fan-out for vision + download (default 2 — vision is metered). */
    concurrency?: number;
    /** Test seam: inject the model call used for captioning. */
    completeFn?: CaptionCompleteFn;
    /** Safety cap on how many assets to derive in one sweep (default unlimited). */
    maxAssets?: number;
    /**
     * Restrict the sweep to a specific set of assetIds. The live drain
     * (AssetIndexQueue) passes the just-enqueued ids so a routine tick only
     * touches new uploads, not the whole workspace; the boot recovery pass omits
     * it and considers every image row. An empty set means "nothing to do" and
     * the sweep returns immediately.
     */
    onlyAssets?: ReadonlySet<string>;
}
export interface AssetIndexSweepResult {
    /** Indexable, in-scope image rows considered. */
    scanned: number;
    /** Captions freshly produced + stored. */
    derived: number;
    /** Assets that errored or produced no caption → recorded `failed`. */
    failed: number;
    /** Already-current (`full`/`failed` at current version) → untouched. */
    skippedUpToDate: number;
    /** Images we couldn't caption (gateway/disabled/non-vision) → left `pending`. */
    leftPending: number;
}
/**
 * Drive the caption / OCR pass for every image asset that needs it. Safe to
 * call fire-and-forget at boot; resilient to per-asset failure.
 */
export declare function sweepAssetIndex(deps: AssetIndexSweepDeps, opts?: AssetIndexSweepOptions): Promise<AssetIndexSweepResult>;
export {};
