/**
 * BatchingApiGatewayArtifactWriter — W0056 prototype B.
 *
 * Drop-in replacement for `ApiGatewayArtifactWriter` that coalesces `emit()`
 * calls into batches, signs many URLs per Vercel round trip via
 * `POST /v1/runs/:runId/artifacts/batch/upload-urls` (W0052), and PUTs to GCS
 * with bounded concurrency.
 *
 * Why: the baseline in `docs/design-docs/artifact-upload-throughput.md` showed
 * that parallel single-URL signs trigger `429 Too Many Requests` on the Vercel
 * signing function. Batch signing eliminates one Vercel round trip per
 * artifact and makes client-side parallelism safe on the API Gateway path.
 *
 * Flushing:
 * - An `emit()` call pushes a `PendingEmit` onto a queue and returns a
 *   deferred Promise. The queue self-flushes via `queueMicrotask` when
 *   the first entry lands, and again whenever the buffer reaches
 *   `batchSize`.
 * - `writeManifest(...)` awaits any in-flight flush and drains the queue
 *   before writing the manifest through a single-URL sign (the manifest
 *   isn't in the registry's per-entry scheme).
 *
 * NDJSON streaming for `traces` is still not implemented here — the
 * upstream `ApiGatewayArtifactWriter` throws `NotImplementedError` and so
 * does this writer. Traces flow through the GCS-direct writer when ADC
 * credentials are present.
 */
import { type ArtifactEntry, type ArtifactRef, type ArtifactType, type ArtifactWriter, type AssociationValues, type RunId, type RunManifest, type WriteSource } from "../_vendor/ailf-core/index.d.ts";
import { type UploadMetricsSink } from "./upload-metrics.js";
export interface BatchingApiGatewayArtifactWriterOptions {
    /** Base URL of the API gateway (e.g., "https://ailf-api.sanity.build"). */
    apiBaseUrl: string;
    /** AILF API key with the `artifact:write` scope. */
    apiKey: string;
    /** GCS bucket name — included in the returned ArtifactRef. */
    bucket: string;
    /**
     * Maximum entries per `/batch/upload-urls` request. The existing Vercel
     * route signs URLs in parallel inside a single Function invocation, so
     * the cap protects the Function's CPU budget. Defaults to 256 (see
     * `DEFAULT_BATCH_SIZE` for the rationale and the W0058 measurement
     * that motivated the bump from 32).
     */
    batchSize?: number;
    /** Bounded concurrency for the PUT fan-out after a batch sign resolves. */
    putConcurrency?: number;
    /** Optional metrics sink; defaults to no-op. */
    metrics?: UploadMetricsSink;
    /**
     * Identifies what kind of execution context owns this writer instance.
     * The D0050 guard refuses to emit a descriptor whose `writePolicy` is
     * different from this value. `"pipeline"` (the default) is for writers
     * driven by an `ailf run` pipeline; `"post-hoc"` is for writers driven
     * by deferred commands like `ailf interpret` that emit descriptors
     * declared `writePolicy: "post-hoc"` (D0050).
     */
    writerSource?: WriteSource;
}
export declare class BatchingApiGatewayArtifactWriter implements ArtifactWriter {
    private readonly options;
    private readonly pending;
    private flushing;
    private microtaskScheduled;
    private readonly writerSource;
    constructor(options: BatchingApiGatewayArtifactWriterOptions);
    emit<T extends ArtifactType>(type: T, association: AssociationValues, payload: unknown): Promise<ArtifactRef | null>;
    appendNdjson(): Promise<ArtifactRef | null>;
    writeManifest(runId: RunId, manifest: RunManifest): Promise<ArtifactRef | null>;
    /** @deprecated — routes through `emit()` for backward compat. */
    writeBulk(type: ArtifactType, runId: RunId, data: unknown): Promise<ArtifactRef | null>;
    /**
     * @deprecated The legacy `writePerEntry` surface requires back-deriving an
     * `AssociationValues` from a wire entryKey, which the registry no longer
     * exposes. Producers on this writer must use `emit()`. All current producers
     * have already migrated.
     */
    writePerEntry(type: ArtifactType, _runId: RunId, _entries: readonly ArtifactEntry[]): Promise<ArtifactRef | null>;
    private scheduleFlush;
    /**
     * Drain the pending queue with overlapped sign + PUT.
     *
     * Pre-pipeline behaviour was `sign(N) → put(N) → sign(N+1) → put(N+1)`.
     * After this change the order becomes
     * `sign(N) → (put(N) || sign(N+1)) → put(N+1)`:
     *
     * - We wait for stage N's sign to resolve, then launch stage N+1's sign
     *   immediately (before awaiting stage N's PUT phase). This is the moment
     *   emits that arrived while stage N's sign was in flight first become
     *   visible to `startNextStage`.
     * - Stage N's PUT fan-out then runs concurrently with stage N+1's sign
     *   request, removing the per-batch sign-RTT pre-amble that made the
     *   W0056 prototype ~10 % slower than the (unsafe) single-URL parallel
     *   variant.
     *
     * Invariants preserved from the pre-pipeline implementation:
     * - Emits resolve in batch-commit order within a stage.
     * - All entries in a `Stage` share one `runId` (enforced by `startNextStage`).
     * - A sign failure fails only that stage's group; later stages proceed.
     * - run() keeps looping until `pending` is empty AND no prefetched stage
     *   remains — so emits that arrive during a PUT phase are picked up in the
     *   next iteration.
     */
    private drain;
    /**
     * Pop the next same-runId batch (up to `batchSize` entries) off the queue
     * and launch its batch-sign request immediately. Returns a `Stage` whose
     * `signPromise` is already in flight, so the caller can start the sign for
     * the *next* batch while this one's PUTs are still pending.
     *
     * Returning `null` means the queue is empty.
     */
    private startNextStage;
    private buildSignBody;
    private batchSign;
    private putToSignedUrl;
    /** Single-URL sign + PUT — used for the manifest. */
    private writeSingle;
}
