/**
 * Type definitions for the unified `apply` primitive (`unified-deploy`
 * capability, gateway routes `POST /apply/v1/plans` + commit + operations).
 *
 * The user-facing inputs accept polymorphic byte sources (strings, Uint8Array,
 * Blob, web streams, plus the Node-only `fileSetFromDir`). The wire shapes
 * carry only `ContentRef` objects — the SDK normalizes byte sources into
 * refs via the CAS content service before issuing the plan request.
 */
import type { NextAction } from "../errors.js";
import type { OperationActorSnapshot } from "./identity-links.types.js";
/**
 * Marker for a filesystem file path. Produced by `fileSetFromDir` from
 * `@run402/sdk/node` so the normalizer can stream-hash from disk without
 * loading bytes into memory.
 *
 * Exposed as a discriminated shape rather than a class so it round-trips
 * through `JSON.parse(JSON.stringify(...))` without losing its identity.
 */
export interface FsFileSource {
    readonly __source: "fs-file";
    readonly path: string;
    readonly contentType?: string;
}
/**
 * Anything the SDK can normalize into a `ContentRef`. Pass UTF-8 strings for
 * text, or raw bytes, `Blob`/`File` (web), web `ReadableStream`, an `FsFileSource`
 * from `fileSetFromDir`, or a wrapper `{ data, contentType? }` for explicit
 * MIME-type control. String sources paired with known binary paths/MIMEs are
 * rejected locally with `BINARY_CONTENT_REQUIRES_BYTES` before any request.
 */
export type ContentSource = string | Uint8Array | ArrayBuffer | Blob | ReadableStream<Uint8Array> | FsFileSource | {
    data: ContentSource;
    contentType?: string;
} | ContentRef;
/**
 * Wire-level reference to a content-addressed object. Produced by hashing a
 * byte source locally and (optionally) negotiating presence via
 * `POST /content/v1/plans`. The `sha256` is lowercase hex; `integrity` is
 * the browser SRI form (`sha256-<base64>`) when emitted.
 */
export interface ContentRef {
    sha256: string;
    size: number;
    contentType?: string;
    integrity?: string;
}
/** A path-keyed set of content. Values may be byte sources (normalized by the
 *  SDK) or already-resolved `ContentRef` objects (from a prior upload). */
export type FileSet = Record<string, ContentSource>;
/**
 * SDK-input sentinel for "walk this directory at submission time." Produced
 * by `dir(path)` from `@run402/sdk/node`. Carried in the public `SiteSpec`
 * types so editors autocomplete `site: { replace: dir("./dist") }` without
 * a cast. The normalizer expands it to a `FileSet` before any plan request;
 * it never leaves the client.
 *
 * The structural shape (not a class) lets the deploy normalizer detect it
 * in the isomorphic SDK without importing the Node-only `assets-node`
 * module that creates it.
 */
export interface LocalDirRef {
    readonly __source: "local-dir";
    readonly path: string;
    readonly prefix?: string;
    readonly ignore?: ReadonlyArray<string>;
    readonly includeSensitive?: boolean;
}
/** Caller-facing spec passed to `r.project(id).apply(spec)`. */
export interface ReleaseSpec {
    /** JSON Schema metadata for editor-authored specs. Stripped before plan requests. */
    $schema?: string;
    /** Project id the release belongs to. */
    project_id: string;
    /** Diff base for the new release. Default: `{ release: "current" }`. Pass
     *  `{ release: "empty" }` for a fresh deploy that should fail if a release
     *  already exists, or `{ release_id: "rel_..." }` to pin a specific base. */
    base?: {
        release: "current" | "empty";
    } | {
        release_id: string;
    };
    database?: DatabaseSpec;
    secrets?: SecretsSpec;
    functions?: FunctionsSpec;
    site?: SiteSpec;
    subdomains?: SubdomainsSpec;
    routes?: ReleaseRoutesSpec;
    checks?: SmokeCheck[];
    /** v1.48 unified-apply: optional asset slice. When present, the apply
     *  promotes per-key blob writes inside the same activation transaction
     *  that flips live_release_id, so asset visibility flips at the exact
     *  moment release visibility flips. */
    assets?: AssetSpec;
    /** v2.5+ routed-locale-context: declare supported locales, a default
     *  locale, and detection sources. The gateway negotiates per request
     *  and surfaces the result to routed HTTP functions via the
     *  `x-run402-locale` / `x-run402-default-locale` request headers.
     *  Omit to carry forward from base; `null` clears the slice. */
    i18n?: I18nSpec | null;
}
/**
 * Asset slice (v1.48 unified-apply). The SDK accepts the ergonomic forms
 * (LocalDirRef from `dir(path)`, `FileSet`, `ContentSource` in `put`) and
 * normalizes to wire-shaped AssetPutEntry[] before submitting. The gateway
 * sees only AssetPutEntry[].
 *
 * - Without `prune`, `sync` is additive (semantically equivalent to
 *   `r.project(id).assets.uploadDir`).
 * - With `prune: true`, the apply is destructive — the SDK runs a plan
 *   first and returns a `PRUNE_CONFIRMATION_REQUIRED` error carrying the
 *   confirm token shape; the caller acknowledges by retrying with
 *   `sync.confirm` populated. The activation transaction's drift check
 *   (HTTP 409 ASSET_SYNC_DRIFT) catches the narrower race where
 *   inventory mutates between commit and activation.
 */
export interface AssetSpec {
    /** Accept either the wire-shaped {@link AssetPutEntry} (`sha256` +
     *  `size_bytes` set, byte upload already happened or about to happen via
     *  pre-uploaded CAS) OR the SDK-input shape {@link AssetPutEntryInput}
     *  (carries `source: ContentSource`; the SDK normalizer hashes the
     *  bytes, registers a byte-reader, and emits wire-shaped entries before
     *  submission). The two forms can be mixed in the same array. */
    put?: (AssetPutEntry | AssetPutEntryInput)[];
    delete?: string[];
    sync?: {
        prefix: string;
        prune: true;
        confirm?: AssetSyncPruneConfirm;
    };
}
export interface AssetPutEntry {
    /** Logical key, e.g. `"static/app.css"`. Same validation rules as
     *  release-file paths (no leading `/`, no `..`, no NUL/control chars,
     *  no `_cas/` or `_staging/` reserved prefixes, ≤1024 bytes total). */
    key: string;
    /** 64 lowercase hex SHA-256 of the bytes. */
    sha256: string;
    /** Size in bytes — must agree with the bytes when the SDK uploads. */
    size_bytes: number;
    /** Defaults to `application/octet-stream`. */
    content_type?: string;
    /** Defaults to `"public"`. Private assets return null for all public URL
     *  fields; obtain a signed URL via `r.project(id).assets.sign(key)`. */
    visibility?: "public" | "private";
    /** Defaults to `true`. Immutable puts populate `internal.asset_versions`
     *  so older content-hashed URLs survive future key mutation/delete (per
     *  the visibility-aware URL matrix). */
    immutable?: boolean;
    /** v1.50 wire: caller-provided flat metadata stored alongside the asset.
     *  Validated client-side (≤4 KB serialized; leaves are
     *  `string | number | boolean | string[]`). */
    metadata?: Record<string, string | number | boolean | string[]>;
    /** v1.50 wire: EXIF retention policy applied to image uploads. Defaults
     *  to `"keep"`. Invalid values are rejected client-side with
     *  `INVALID_EXIF_POLICY`. */
    exif_policy?: "keep" | "strip";
}
/**
 * SDK-input shape for an asset put. Carries a `ContentSource` rather than
 * a pre-computed `sha256` / `size_bytes`; the SDK normalizer hashes the
 * source, registers a byte-reader in the apply's `byteReaders` map (keyed
 * by SHA), and emits a wire-shaped {@link AssetPutEntry} before the spec
 * is sent to `/apply/v1/plans`. The `source` field NEVER reaches the
 * gateway.
 *
 * Discriminated from {@link AssetPutEntry} by the presence of `source`
 * and the absence of `sha256`. Either form may appear in
 * `AssetSpec.put`.
 */
export interface AssetPutEntryInput {
    key: string;
    /** Bytes to upload. Accepts every shape `resolveContent` accepts —
     *  `string`, `Uint8Array`, `ArrayBuffer`, `Blob`, `FsFileSource`,
     *  `ReadableStream`, `{ data, contentType }` wrapper, `{ content }`,
     *  `{ bytes }`. */
    source: ContentSource;
    content_type?: string;
    visibility?: "public" | "private";
    immutable?: boolean;
    /** v1.50: caller-provided flat metadata stored alongside the asset.
     *  Validated client-side (≤4 KB serialized; leaves are
     *  `string | number | boolean | string[]`). Rejected with
     *  `INVALID_ASSET_METADATA` for nested objects / non-allowed leaves. */
    metadata?: Record<string, string | number | boolean | string[]>;
    /** v1.50: EXIF retention policy applied to image uploads. Default
     *  `"keep"`. Invalid values are rejected client-side with
     *  `INVALID_EXIF_POLICY`. The normalizer converts this camelCase input
     *  to the wire-shape `exif_policy`. */
    exifPolicy?: "keep" | "strip";
}
export interface AssetSyncPruneConfirm {
    /** SHA-256 hex returned by a prior `r.project(id).apply.plan` call. */
    base_revision: string;
    /** SHA-256 hex of the canonical-form sorted planned delete-key list. */
    delete_set_digest: string;
    /** Expected count of keys to delete. */
    expected_delete_count: number;
}
export interface DatabaseSpec {
    migrations?: MigrationSpec[];
    /** Declarative authorization manifest applied during deploy. */
    expose?: ExposeManifest;
    /** Opt-in: skip the migrate-gate phase. Only safe when migrations are
     *  declared backward-compatible (no breaking schema changes). */
    zero_downtime?: boolean;
}
export type MigrationSpec = VersionedMigrationSpec | ContentTrackedMigrationSpec;
export interface BaseMigrationSpec {
    /** Lowercase hex SHA-256 of the migration SQL. Computed by the SDK from
     *  `sql` if not provided. */
    checksum?: string;
    /** Inline SQL (UTF-8). The SDK uploads to CAS and replaces with `sql_ref`
     *  before the plan request. Either `sql` or `sql_ref` is required. */
    sql?: string;
    /** Pre-uploaded SQL CAS reference. */
    sql_ref?: ContentRef;
    /** Default `"required"` — runs in a single advisory-locked transaction.
     *  `"none"` opts out (and on failure, sends the operation to
     *  `needs_repair` rather than rolling back). */
    transaction?: "required" | "none";
}
export interface VersionedMigrationSpec extends BaseMigrationSpec {
    /** Stable migration id, e.g. `"001_init"`. Same id + same checksum across
     *  re-deploys is a registry noop; same id + different checksum is a hard
     *  error. Use `name` instead for generated/idempotent SQL whose identity
     *  should track content changes. */
    id: string;
    name?: never;
}
export interface ContentTrackedMigrationSpec extends BaseMigrationSpec {
    /** Content-tracked migration name for generated/idempotent SQL. The SDK
     *  compiles this to `id = <name>_<sha256(sql)[0:16]>`; changed content
     *  applies once under a new id, identical re-deploys noop. SQL declared
     *  with `name` MUST be idempotent because it re-runs whenever content
     *  changes against a database where prior versions may already exist. */
    name: string;
    id?: never;
}
export type ExposePolicy = "user_owns_rows" | "public_read_authenticated_write" | "public_read_append_only" | "public_read_write_UNRESTRICTED" | "custom";
export interface EffectiveAccessPreview {
    table: string;
    role: "anon" | "authenticated";
    declared_operations: string[] | null;
    required_privileges: string[] | null;
    validation: "pending" | "structural";
    row_checks: "runtime";
    findings?: string[];
    added_privileges?: string[];
}
/** Declarative authorization manifest. Pass-through shape — the gateway
 *  validates the schema. See https://run402.com/schemas/manifest.v1.json. */
export interface ExposeManifest {
    version?: string;
    tables?: Array<Record<string, unknown>>;
    views?: Array<Record<string, unknown>>;
    rpcs?: Array<Record<string, unknown>>;
    [key: string]: unknown;
}
export interface SecretsSpec {
    /** Keys that must already exist in the project's secret store at commit time.
     *  Plan-time emits a MISSING_REQUIRED_SECRET warning for absent keys;
     *  commit-time hard-errors if they are still missing. */
    require?: string[];
    /** Delete specific secrets by key at activation. Unknown keys hard-error at commit-time gating. */
    delete?: string[];
}
export interface FunctionsSpec {
    /** The new desired set — functions absent here are removed in the new
     *  release. */
    replace?: Record<string, FunctionSpec>;
    /** Surgical updates — only listed functions change. */
    patch?: {
        set?: Record<string, FunctionSpec>;
        delete?: string[];
    };
}
export interface FunctionSpec {
    runtime?: "node22";
    /** Bundled source (single file). Mutually exclusive with `files`. */
    source?: ContentSource;
    /** Multi-file function (entrypoint + assets). Provide `entrypoint` when
     *  using this shape. */
    files?: FileSet;
    /** Required when `files` is set. The relative path of the entrypoint
     *  within the file set (e.g., `"index.mjs"`). */
    entrypoint?: string;
    config?: {
        timeoutSeconds?: number;
        memoryMb?: number;
    };
    /**
     * Capability `apply-v1-function-deps`. Additional npm packages to install
     * and bundle with the function. Each entry is an npm spec: a bare name
     * (`"lodash"`) resolves to latest at deploy time; a pinned spec
     * (`"lodash@4.17.21"`) or range (`"date-fns@^3.0.0"`) is honored verbatim.
     * `@run402/functions` (auto-bundled) and native-binary modules are rejected
     * by the gateway. Omit for no user dependencies. The resolved concrete
     * versions live on the function record (read via the GET-list), not the
     * apply/deploy result.
     */
    deps?: string[];
    /** Durable function triggers. Schedule triggers create durable function runs. */
    triggers?: FunctionTriggerSpec[];
    /** Legacy direct schedule field. Prefer `triggers[]` schedule entries for new manifests. */
    schedule?: string | null;
    /**
     * v1.52+: function class. When `"ssr"`, the gateway provisions the
     * Lambda with SnapStart enabled and routes invocations through the
     * origin ISR cache. Defaults to `"standard"` when omitted. Set by
     * `@run402/astro` (and other framework adapters) for server-rendering
     * entry functions; user-authored functions should usually omit this
     * and let the gateway default.
     */
    class?: "ssr" | "standard";
    /**
     * Function capability declarations interpreted by the gateway/runtime.
     * Framework adapters use this for runtime contracts such as
     * `astro.ssr.v1`; auth helpers can use it for explicit trust gates.
     */
    capabilities?: string[];
    /**
     * v1.51+: when `true`, the Run402 gateway rejects callers without a
     * valid project user JWT with `401` before invoking the function.
     * Independent from `requireRole` — set `requireRole` alone to imply
     * authentication, or set `requireAuth: true` alone for a session
     * check with no DB lookup.
     *
     * When the gate passes, the gateway injects `x-run402-user-id` into
     * the request. In-function code reads it with `getUserId(req)` from
     * `@run402/functions`.
     *
     * Gateway is authoritative — the SDK does not validate shape (use
     * the canonical `INVALID_SPEC` envelope from the gateway at plan
     * time for typos).
     */
    requireAuth?: boolean;
    /**
     * v1.51+: declarative application-role gate. When set, the gateway
     * resolves the caller's role from the project-schema table named in
     * `table` (FK column `idColumn` matches the JWT `sub`, role string
     * read from `roleColumn`) and rejects callers whose role is not in
     * `allowed` with `403`. Lookup is RLS-bypass (the gateway is the
     * trusted intermediary). Per-`(projectId, userId)` cache with
     * configurable TTL.
     *
     * `requireRole` implies authentication — no valid JWT → `401` (the
     * same envelope as `requireAuth`). All `requireRole` blocks in a
     * single release must share the same `(table, idColumn, roleColumn)`
     * triple (gateway rejects mixed tables at plan time).
     *
     * When the gate passes, the gateway injects both `x-run402-user-id`
     * AND `x-run402-user-role` into the request. In-function code reads
     * them with `getUserId(req)` / `getRole(req)` from
     * `@run402/functions`.
     *
     * Pass `null` in `patch.set` to remove an existing gate; omit to
     * leave it unchanged.
     */
    requireRole?: RequireRoleSpec | null;
}
export interface FunctionTriggerRunSpec {
    event_type: string;
    payload?: Record<string, unknown>;
    retry?: Record<string, unknown>;
    expires_after_seconds?: number;
}
export interface FunctionScheduleTriggerSpec {
    id: string;
    type: "schedule";
    cron: string;
    timezone?: string;
    misfire_policy?: "skip";
    overlap_policy?: "allow";
    run: FunctionTriggerRunSpec;
}
/**
 * `mailbox_suspended` (recovery-event-reachability, gateway 2026-07): fires
 * when the mailbox is abuse-suspended, so the project's own agent observes
 * the suspension as a durable function run — no public callback URL, no
 * polling. The run payload carries `{event: {mailbox_id, suspended_reason,
 * suspended_at, evidence, recovery_actions}}` and executes independently of
 * the suspended mailbox's send capability.
 */
export type EmailTriggerEvent = "reply_received" | "delivery" | "bounced" | "complained" | "mailbox_suspended";
export interface FunctionEmailTriggerSpec {
    id: string;
    type: "email";
    /** Mailbox id or slug to subscribe to. */
    mailbox: string;
    /** Email events that should create durable function runs. */
    events: EmailTriggerEvent[];
    run: FunctionTriggerRunSpec;
}
export type FunctionTriggerSpec = FunctionScheduleTriggerSpec | FunctionEmailTriggerSpec;
/**
 * v1.51+: declarative role-gate descriptor for `FunctionSpec.requireRole`.
 *
 * The gateway runs a `SELECT roleColumn FROM <project_schema>.table WHERE
 * idColumn = $jwt.sub` lookup (RLS-bypass), byte-equality-checks the
 * result against `allowed`, and caches the answer per
 * `(projectId, userId)` for `cacheTtl` seconds.
 *
 * Identifiers are unquoted; schema-qualified names (e.g. `"public.members"`)
 * are rejected at plan time with `INVALID_SPEC`. The project schema is
 * resolved server-side from the project record — do not include it here.
 */
export interface RequireRoleSpec {
    /** Project-schema table holding role rows (e.g., `"members"`).
     *  Unqualified. */
    table: string;
    /** Column in `table` that matches the JWT `sub` claim (the user id).
     *  Typically `"user_id"`. */
    idColumn: string;
    /** Column in `table` holding the role string. Typically `"role"`. */
    roleColumn: string;
    /** Allowed role values. Case-sensitive byte equality. Non-empty.
     *  Multi-element arrays permit any matching role (OR semantics). */
    allowed: string[];
    /** Cache TTL in seconds for the resolved role per
     *  `(projectId, userId)`. Default `60`. Max `600`. Set `0` to disable
     *  caching (fresh lookup on every request) — use for high-stakes
     *  operations where instant demotion matters. A demoted user retains
     *  the cached role until expiry; demotion is NOT broadcast. */
    cacheTtl?: number;
    /** Unauthorized HTML response mode. Default gateway behavior is an error
     *  envelope; `"redirect"` sends unauthenticated browser requests to
     *  `signInPath` for console-style apps. */
    onDeny?: "envelope" | "redirect";
    /** Same-origin sign-in path used when `onDeny` is `"redirect"`. */
    signInPath?: string;
}
export interface PublicStaticPathSpec {
    /** Release static asset path, e.g. "events.html". This is not a public URL. */
    asset: string;
    cache_class?: StaticCacheClass;
}
export type SitePublicPathsSpec = {
    mode: "implicit";
    replace?: never;
} | {
    mode: "explicit";
    replace: Record<string, PublicStaticPathSpec>;
};
/** tenant-site-embedding: a platform embedding catalog key. `localhost` expands
 *  to `http://localhost:*` and `http://127.0.0.1:*`. Raw origins are never
 *  accepted; the gateway rejects them with INVALID_SPEC naming the valid keys.
 *  Typed as the known literal plus any string so a new catalog key never needs
 *  a client release (preserve unknown future strings). */
export type EmbeddingKey = "localhost" | (string & {});
/** tenant-site-embedding: who may put the site in an iframe. Omitted on a later
 *  apply carries the base release's declaration forward; `null` clears it back
 *  to the default deny (`frame-ancestors 'none'` + `X-Frame-Options: DENY`). */
export interface SiteEmbeddingSpec {
    frame_ancestors: EmbeddingKey[];
}
export type SiteSpec = {
    replace: FileSet | LocalDirRef;
    patch?: never;
    public_paths?: SitePublicPathsSpec;
    embedding?: SiteEmbeddingSpec | null;
    sign_in_path?: string | null;
    noindex?: boolean | null;
} | {
    patch: {
        put?: FileSet | LocalDirRef;
        delete?: string[];
    };
    replace?: never;
    public_paths?: SitePublicPathsSpec;
    embedding?: SiteEmbeddingSpec | null;
    sign_in_path?: string | null;
    noindex?: boolean | null;
} | {
    public_paths: SitePublicPathsSpec;
    replace?: never;
    patch?: never;
    embedding?: SiteEmbeddingSpec | null;
    sign_in_path?: string | null;
    noindex?: boolean | null;
} | {
    embedding: SiteEmbeddingSpec | null;
    replace?: never;
    patch?: never;
    public_paths?: never;
    sign_in_path?: string | null;
    noindex?: boolean | null;
} | {
    sign_in_path: string | null;
    replace?: never;
    patch?: never;
    public_paths?: never;
    embedding?: never;
    noindex?: boolean | null;
} | {
    noindex: boolean | null;
    replace?: never;
    patch?: never;
    public_paths?: never;
    embedding?: never;
    sign_in_path?: never;
};
export interface SubdomainsSpec {
    /** The exact desired set. Currently limited to one element per project —
     *  the gateway returns `SUBDOMAIN_MULTI_NOT_SUPPORTED` for multi-element
     *  arrays. */
    set?: string[];
    /** Add specific subdomains without disturbing others. */
    add?: string[];
    /** Remove specific subdomains. */
    remove?: string[];
}
/**
 * Where the gateway looks for the caller's locale during routed-function
 * negotiation. Walked in order; the first match wins. Sources:
 *
 * - `"accept-language"` — parse per RFC 9110, drop `q=0` and `*`, sort by
 *   q descending (stable on original order for ties); apply RFC 4647 §3.4
 *   lookup-style truncation (`zh-Hant-TW` → `zh-Hant` → `zh`); longest
 *   matching prefix wins. A generic request tag does NOT match a more
 *   specific `locales[]` entry (`Accept-Language: es` does NOT match
 *   `locales: ["es-MX"]`).
 * - `cookie:<name>` — case-sensitive cookie name lookup; raw value (no
 *   percent-decode) matched case-insensitively against `locales[]`.
 *   Cookie name MUST match RFC 6265 grammar
 *   (`/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/`).
 */
export type I18nDetectSource = "accept-language" | `cookie:${string}`;
/** Backwards-friendly alias — the issue and gateway both call this `DetectSource`. */
export type DetectSource = I18nDetectSource;
/**
 * Routed-locale-context release slice (v2.5+). Drives the negotiated
 * locale that the gateway surfaces to routed HTTP function invocations
 * via the `x-run402-locale` and `x-run402-default-locale` request headers.
 *
 * Carry-forward semantics (simpler than `routes` — no `{ replace }`
 * envelope):
 *
 * - `i18n` omitted from a ReleaseSpec → carry forward from base release.
 * - `i18n: null` → clear the slice on the new release.
 * - `i18n: { ... }` → replace the slice with the provided value.
 *
 * The negotiated locale is returned in the canonical casing supplied here
 * in `locales[]`, NOT the request's casing. Static-route hits do NOT
 * receive locale negotiation; only routed HTTP function invocations do.
 * Run402 does NOT inject `Vary` headers — apps that return public-
 * cacheable responses varying by locale must set their own `Vary`.
 */
export interface I18nSpec {
    /** Default locale tag. MUST be byte-identical to one entry in
     *  `locales[]`. The platform does NOT silently canonicalize. */
    defaultLocale: string;
    /** Supported locale tags. Non-empty, ≤50 entries. Each tag MUST match
     *  `/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/`. Tags are opaque — there is
     *  NO BCP-47 semantic validation. */
    locales: string[];
    /** Detection sources, walked in order. Default `["accept-language"]`
     *  when omitted, ≤10 entries. `[]` is allowed and means "always
     *  default". */
    detect?: I18nDetectSource[];
    /** How the gateway handles an otherwise-valid locale value that is not in
     *  `locales[]`. Omit for the gateway default. */
    unknownLocalePolicy?: "default" | "reject";
}
/** Materialized form of `I18nSpec` as it appears on release inventory
 *  reads (`releases/active`, `releases/:id`). Same shape as `I18nSpec`
 *  but with `detect` always populated — the gateway materializes the
 *  default `["accept-language"]` when the spec omitted it, and preserves
 *  `[]` verbatim. Use this when reading deployed state; use `I18nSpec`
 *  when constructing a `ReleaseSpec`. */
export interface ReleaseInventoryI18n {
    defaultLocale: string;
    locales: string[];
    detect: I18nDetectSource[];
}
export declare const ROUTE_HTTP_METHODS: readonly ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"];
export type RouteHttpMethod = (typeof ROUTE_HTTP_METHODS)[number];
export declare const ROUTE_PRICING_NETWORKS: readonly ["mainnet", "testnet", "lightning", "lightning-testnet", "tempo", "tempo-testnet"];
export declare const LIGHTNING_ROUTE_PRICING_NETWORKS: readonly RoutePricingNetwork[];
export type RoutePricingNetwork = (typeof ROUTE_PRICING_NETWORKS)[number];
export interface RoutePricingSpec {
    mode: "always";
    amount_usd_micros: number;
    /**
     * Pins the Lightning price in millisatoshis for the route's `lightning` /
     * `lightning-testnet` entries; needs a Lightning network. Absent, each
     * challenge quotes `amount_usd_micros` into whole satoshis.
     */
    amount_msat?: number;
    pay_to: "org_default_payout";
    /**
     * Omit to accept production mainnet only. Add `"testnet"` explicitly for
     * testnet acceptance. `"lightning"` / `"lightning-testnet"` sell for sats
     * over x402 `exact` on `lnbtc`, settled to the organization's Lightning
     * receiver (`run402 orgs lightning-receiver set`); a Lightning-only route
     * needs no EVM payout wallet. `"tempo"` / `"tempo-testnet"` also take an
     * MPP Tempo charge in pathUSD (`WWW-Authenticate: Payment`), paid to the
     * organization's payout address on Tempo mainnet / Moderato.
     */
    networks?: readonly RoutePricingNetwork[];
    /** Ask the function to declare committed fulfillment for a signed merchant receipt. */
    receipt?: "on_fulfillment";
    /**
     * Opt in to the x402 Bazaar: the 402 challenge carries this metadata and the
     * first paid call lists the route. Exact routes with one method only.
     */
    discovery?: RoutePricingDiscovery;
}
export interface RoutePricingDiscovery {
    /** At most 500 characters, used verbatim. */
    description: string;
    /** Default `application/json`. */
    mime_type?: string;
    /** 1-32 printable ASCII characters. */
    service_name?: string;
    /** At most 4, each 1-32 printable ASCII; Run402 appends `run402`. */
    tags?: readonly string[];
    /** Absolute https URL on a public host name. */
    icon_url?: string;
    /** Query parameters for GET/HEAD/DELETE, a body (`body_type` required) for POST/PUT/PATCH. */
    input?: {
        query_params_schema?: Record<string, unknown>;
        body_type?: "json" | "form-data" | "text";
        body_schema?: Record<string, unknown>;
        example?: unknown;
    };
    output?: {
        example?: unknown;
        schema?: Record<string, unknown>;
    };
}
export interface FunctionRouteTarget {
    type: "function";
    /** Materialized release function name, not a file name or handler export. */
    name: string;
}
export interface StaticRouteTarget {
    type: "static";
    /** Materialized static-site file path, relative to the site root. */
    file: string;
}
export type RouteTarget = FunctionRouteTarget | StaticRouteTarget;
/** One apply-v1 web route entry. */
export interface RouteSpec {
    pattern: string;
    /** Omit to allow every supported method. Empty arrays are invalid. */
    methods?: readonly RouteHttpMethod[];
    target: RouteTarget;
    /** Fixed-price tenant x402 policy for function routes. Static routes cannot be priced. */
    pricing?: RoutePricingSpec;
}
/** Top-level release route resource. Omit or pass null to carry routes forward. */
export type ReleaseRoutesSpec = null | {
    replace: RouteSpec[];
};
export interface SmokeCheck {
    name: string;
    http?: {
        path: string;
        method?: string;
        expect?: {
            status?: number;
        };
    };
}
type LiteralUnion<T extends string> = T | (string & {});
export type KnownStaticCacheClass = "html" | "immutable_versioned" | "revalidating_asset";
export type StaticCacheClass = LiteralUnion<KnownStaticCacheClass>;
export interface StaticManifestMetadata {
    file_count: number;
    total_bytes: number;
    cache_classes: Record<string, number>;
    cache_class_sources: Record<string, number>;
    spa_fallback: string | null;
}
export declare const EMPTY_STATIC_MANIFEST_METADATA: StaticManifestMetadata;
export declare function normalizeStaticManifestMetadata(metadata: StaticManifestMetadata | null | undefined): StaticManifestMetadata;
export interface StaticAssetsDiff {
    unchanged: number;
    changed: number;
    added: number;
    removed: number;
    newly_uploaded_cas_bytes: number;
    reused_cas_bytes: number;
    deployment_copy_bytes_eliminated: number;
    legacy_immutable_warnings: Array<{
        path: string;
        sha256: string;
        reason: string;
    }>;
    previous_immutable_failures: Array<{
        path: string;
        previous_sha256: string;
        candidate_sha256: string;
    }>;
    cas_authorization_failures: string[];
}
export type DeployResolveMethod = RouteHttpMethod | (string & {});
export type KnownDeployResolveMatch = "host_missing" | "manifest_missing" | "active_release_missing" | "unsupported_manifest_version" | "path_error" | "none" | "static_exact" | "static_index" | "spa_fallback" | "retained_public_path" | "spa_fallback_missing" | "route_function" | "route_static_alias" | "route_method_miss" | "deployment_host" | "deployment_retired";
export type DeployResolveMatch = LiteralUnion<KnownDeployResolveMatch>;
export type KnownDeployResolveAuthorizationResult = "authorized" | "not_public" | "not_applicable" | "manifest_missing" | "target_missing" | "active_release_missing" | "unsupported_manifest_version" | "path_error" | "missing_cas_object" | "unfinalized_or_deleting_cas_object" | "size_mismatch" | "unauthorized_cas_object";
export type DeployResolveAuthorizationResult = LiteralUnion<KnownDeployResolveAuthorizationResult>;
export type KnownDeployResolveFallbackState = "unavailable" | "active_release_missing" | "unsupported_manifest_version" | "negative_cache_hit" | "path_error" | "method_not_static" | "not_used" | "target_missing" | "used" | "not_configured" | "not_eligible";
export type DeployResolveFallbackState = LiteralUnion<KnownDeployResolveFallbackState>;
export type KnownDeployResolveResult = 200 | 400 | 404 | 405 | 503;
export interface DeployResolveRouteMatch {
    pattern: string;
    methods: RouteHttpMethod[] | string[] | null;
    target: RouteTarget;
}
export interface DeployResolveCasObject {
    sha256: string;
    exists: boolean;
    expected_size: number;
    actual_size?: number | null;
    [key: string]: unknown;
}
export interface DeployResolveResponseVariant {
    kind: string;
    varies_by: string | string[];
    hostname: string;
    release_id: string | null;
    release_generation: number | null;
    path: string;
    raw_static_sha256: string;
    variant_inputs_hash: string;
    [key: string]: unknown;
}
export type DeployResolveOptions = {
    project: string;
    url: string | URL;
    method?: DeployResolveMethod;
    host?: never;
    path?: never;
} | {
    project: string;
    host: string;
    path?: string;
    method?: DeployResolveMethod;
    url?: never;
};
export type ScopedDeployResolveOptions = (Omit<Extract<DeployResolveOptions, {
    url: string | URL;
}>, "project"> & {
    project?: string;
}) | (Omit<Extract<DeployResolveOptions, {
    host: string;
}>, "project"> & {
    project?: string;
});
export interface NormalizedDeployResolveRequest {
    project: string;
    project_scope: "credential_lookup_only";
    project_sent_to_gateway: false;
    original_url?: string;
    host: string;
    path: string;
    method?: string;
    ignored?: {
        query?: string;
        fragment?: string;
    };
}
export interface DeployResolveResponse {
    static_continuity?: StaticContinuity;
    current_host_release_id?: string | null;
    current_host_release_generation?: number | null;
    hostname: string;
    host_binding_id?: string | null;
    binding_status?: string | null;
    project_id?: string | null;
    channel?: string | null;
    release_id?: string | null;
    release_generation?: number | null;
    route_manifest_sha256?: string | null;
    static_manifest_sha256?: string | null;
    static_manifest_metadata?: StaticManifestMetadata | null;
    /** tenant-site-embedding: the framing opt-in of the release this host serves
     *  (catalog keys) or `null`; absent on an older gateway. */
    embedding?: SiteEmbeddingSpec | null;
    /** site.noindex of the release this host serves; absent on an older gateway. */
    noindex?: boolean;
    normalized_path?: string | null;
    match: DeployResolveMatch;
    route?: DeployResolveRouteMatch | null;
    asset_path?: string | null;
    reachability_authority?: StaticReachabilityAuthority | null;
    direct?: boolean | null;
    static_sha256?: string | null;
    content_type?: string | null;
    cache_class?: StaticCacheClass | null;
    cache_policy?: string | null;
    authorization_result?: DeployResolveAuthorizationResult | null;
    cas_object?: DeployResolveCasObject | null;
    response_variant?: DeployResolveResponseVariant | null;
    allow?: RouteHttpMethod[] | string[] | null;
    route_pattern?: string | null;
    target_type?: LiteralUnion<"function" | "static"> | null;
    target_name?: string | null;
    target_file?: string | null;
    authorized: boolean;
    fallback_state: DeployResolveFallbackState;
    error_code?: string | null;
    legacy_immutable_risk?: Array<Record<string, unknown>>;
    emergency_fallback?: Record<string, unknown> | null;
    edge_propagation?: EdgePropagationDiagnostics | null;
    /** `deployment` when the host is a deployment host (`dpl-*.sites.run402.com`). */
    host_kind?: LiteralUnion<"deployment"> | null;
    /** Deployment hosts: the `dpl_` deployment the host serves. */
    deployment_id?: string | null;
    /** Deployment hosts: `live` (the active release or a subdomain serves it),
     *  `retained` (replaced; serves until `origin_available_until`), or
     *  `retired` (no longer served). */
    deployment_status?: LiteralUnion<"live" | "retained" | "retired"> | null;
    /** Deployment hosts: when a replaced deployment stops (or stopped) serving; null while live. */
    origin_available_until?: string | null;
    /** Deployment hosts: how long a replaced deployment keeps serving. */
    origin_retention_seconds?: number | null;
    /** Deployment hosts: `true` when the path was resolved against the deployment's
     *  release (its static manifest and public paths); `false` for a retired host or
     *  a deployment no release names, which is reported per host. */
    path_evaluated?: boolean;
    /** Deployment hosts: `false` once the deployment was replaced; its routes no
     *  longer run there, and a matched route reports result 404. */
    routes_active?: boolean;
    /** Diagnostic body status. This is not necessarily the HTTP response status. */
    result: number;
    [key: string]: unknown;
}
export type EdgePropagationStatus = "settled" | "propagating" | "sync_pending";
export type EdgePropagationSource = "present" | "missing" | "unknown";
export interface EdgePropagationDiagnostics {
    binding: string;
    claimed_at: string;
    kvs_synced_at: string | null;
    kvs_source: EdgePropagationSource;
    status: EdgePropagationStatus;
    expected_visible_by: string | null;
    hint: string;
}
export interface SubdomainBindingFreshness {
    host: string;
    claimed_at: string;
    kvs_synced_at: string | null;
}
export interface DeployResolveWarning {
    code: string;
    message: string;
}
export interface DeployResolveNextStep {
    code: string;
    message: string;
}
export interface DeployResolveSummary {
    would_serve: boolean;
    diagnostic_status: number;
    match: DeployResolveMatch;
    category: string;
    summary: string;
    warnings: DeployResolveWarning[];
    next_steps: DeployResolveNextStep[];
}
export declare function normalizeDeployResolveRequest(opts: DeployResolveOptions): NormalizedDeployResolveRequest;
export declare function isDeployResolveStaticHit(response: DeployResolveResponse): response is DeployResolveResponse & {
    match: "static_exact" | "static_index" | "spa_fallback" | "retained_public_path";
};
export declare function isDeployResolveRouteHit(response: DeployResolveResponse): response is DeployResolveResponse & {
    route: DeployResolveRouteMatch;
};
export declare function buildDeployResolveSummary(response: DeployResolveResponse, request: NormalizedDeployResolveRequest): DeployResolveSummary;
export declare function summarizeDeployResult(result: DeployResult): DeploySummary;
export declare function isModernPlanMigrationDiff(value: unknown): value is PlanMigrationDiff;
export interface PlanResponse {
    static_continuity?: StaticContinuity;
    effective_access?: EffectiveAccessPreview[];
    /** Present on the v2 plan envelope. Older gateways omitted it; the SDK
     *  preserves backward compatibility and still normalizes both shapes. */
    kind?: "plan_response";
    schema_version?: "agent-deploy-observability.v1";
    actor?: OperationActorSnapshot | null;
    /** Null only for local/legacy dry-run and reviewed-plan preview responses. */
    plan_id: string | null;
    /** Null only for local/legacy dry-run and reviewed-plan preview responses. */
    operation_id: string | null;
    /** Gateway-reviewed plan identity. Present for reviewed plan mode. */
    plan_fingerprint?: string | null;
    /** ISO timestamp when a reviewed plan expires. Present for reviewed plan mode. */
    plan_expires_at?: string | null;
    planner_semantics_version?: string | null;
    base_identity?: string | null;
    next_actions?: unknown[];
    /** vault (protocol §6.5). Present only when the plan declared a capture. */
    vault?: {
        /** The canonical `apply_plan_canonical/v1` digest the activation token binds. */
        apply_plan_sha256?: string | null;
    } | null;
    base_release_id: string | null;
    manifest_digest: string;
    is_noop?: boolean;
    summary?: string;
    expected_events?: string[];
    /** Per-ref presence list. The gateway reports which content SHAs the
     *  project already has and which need to be uploaded. Items with
     *  `present: false` must be uploaded via `POST /content/v1/plans` before
     *  the deploy commit will succeed. */
    missing_content: PlanContentRef[];
    /** SDK-normalized diff convenience. New gateways return these buckets at
     *  top level; `normalizePlanResponse` folds them back into `diff` so older
     *  callers and event consumers keep working. */
    diff: DeployDiff;
    warnings: WarningEntry[];
    payment_required?: PaymentRequiredHint | null;
    migrations?: PlanMigrationDiff;
    site?: SiteDiff;
    functions?: FunctionsDiff;
    secrets?: SecretsDiff;
    subdomains?: SubdomainsDiff;
    routes?: RoutesDiff;
    static_assets?: StaticAssetsDiff;
    /** v1.48 unified-apply: one entry per `assets.put` item, with the
     *  AssetRef envelope resolved at plan time (URLs are deterministic
     *  from `(project_public_id, key, content_sha256)` so they're
     *  computable BEFORE commit). Agents that need URLs
     *  pre-commit (for HTML injection between plan and commit) read
     *  these directly. */
    asset_entries?: AssetEntryPlanResult[];
    /** v1.48 unified-apply: echo of `assets.delete`. */
    asset_deletes?: string[];
    /** v1.48 unified-apply: destructive confirmation block when the spec
     *  declared `assets.sync.prune: true`. Carries the values the caller
     *  must echo back in commit's `assets.sync.confirm` to authorize the
     *  destructive operation. `sample_keys` is capped at 50 inline;
     *  larger sets set `over_inline_threshold: true`. Design D10. */
    asset_sync?: AssetSyncPlanBlock;
    /** v1.48 unified-apply: structured cost preview. Design D18. */
    cost?: PlanCostPreview;
    /** v1.111+ rehearsal affordance for migration-bearing plans. */
    rehearsal?: PlanRehearsalEnvelope;
}
export interface PlanRehearsalEnvelope {
    /** True for a persisted, migration-bearing plan on a project with a live
     *  release to protect. `apply()` rehearses automatically when true. */
    available: boolean;
    rehearse_url: string | null;
    /** Why rehearsal is not offered: `no_migrations` (nothing to rehearse),
     *  `no_live_release` (a first deploy has nothing to branch from — commit
     *  directly), `migrations_unchanged` (every declared migration is a
     *  checksum-identical noop already applied to the live project — nothing
     *  would run on the branch), or `ci_session_unsupported` (the plan was made
     *  by a CI session, which the rehearse route refuses — commit directly).
     *  `null` when available. */
    reason: null | "no_migrations" | "no_live_release" | "migrations_unchanged" | "ci_session_unsupported";
    /** Present with `reason: "no_live_release"` / `"migrations_unchanged"` /
     *  `"ci_session_unsupported"`: one `commit_plan` entry. */
    next_actions?: Array<{
        type: string;
        command?: string;
        why: string;
    }>;
}
/** How `apply()` handled rehearsal for this deploy (first-deploy-agent-dx). */
export interface DeployRehearsalBlock {
    status: "passed" | "skipped" | "failed";
    /** Set when `status` is `skipped`. `no_live_release`: first deploy;
     *  `no_migrations`: nothing to rehearse; `migrations_unchanged`: every
     *  declared migration is a checksum-identical noop already applied (the
     *  plan's `migrations.new` bucket is empty), so a rehearsal would run
     *  nothing; `ci_session_unsupported`: a CI session's plan, which the
     *  gateway does not rehearse; `disabled`: `noRehearse`; `reviewed_plan`: a `requiredPlan`
     *  was supplied (already reviewed); `unsupported`: the target (Core) has
     *  no branches. */
    reason?: "no_migrations" | "no_live_release" | "migrations_unchanged" | "ci_session_unsupported" | "disabled" | "reviewed_plan" | "unsupported";
    /** The gateway's rehearsal report when a rehearsal ran. */
    report?: ApplyRehearsalReport;
    operation_id?: string;
    branch_project_id?: string | null;
}
/** Resolved AssetRef envelope per `assets.put` entry at plan time. */
export interface AssetEntryPlanResult {
    key: string;
    sha256: string;
    size_bytes: number;
    content_type: string;
    visibility: "public" | "private";
    immutable: boolean;
    status: "present" | "satisfied_by_plan" | "upload_pending";
    asset_ref: ResolvedAssetRef;
}
/** Wire-shape variant entry returned by the gateway plan response (v1.49+). */
export interface ResolvedAssetVariant {
    url: string;
    cdn_url: string;
    width_px: number;
    height_px: number;
    format: "webp" | "jpeg";
    sha256: string;
}
export interface ResolvedAssetRef {
    key: string;
    sha256: string;
    size_bytes: number;
    content_type: string;
    visibility: "public" | "private";
    immutable: boolean;
    url: string | null;
    immutable_url: string | null;
    cdn_url: string | null;
    cdn_immutable_url: string | null;
    sri: string | null;
    etag: string;
    content_digest: string;
    width_px?: number;
    height_px?: number;
    blurhash?: string;
    variant_spec_version?: string;
    display_url?: string;
    display_immutable_url?: string;
    variants?: {
        thumb?: ResolvedAssetVariant;
        medium?: ResolvedAssetVariant;
        large?: ResolvedAssetVariant;
        display_jpeg?: ResolvedAssetVariant;
    };
    metadata?: Record<string, string | number | boolean | string[]> | null;
    image_format?: string | null;
    image_info?: Record<string, unknown> | null;
    image_exif?: Record<string, unknown> | null;
    image_exif_policy?: "keep" | "strip" | null;
    blurhash_data_url?: string | null;
    asset_schema?: "v1.49" | "v1.50" | "v1.54" | null;
}
export interface AssetSyncPlanBlock {
    prefix: string;
    prune: true;
    base_revision: string;
    delete_set_digest: string;
    expected_delete_count: number;
    sample_keys: string[];
    over_inline_threshold: boolean;
}
export interface PlanCostPreview {
    storage_bytes_added: number;
    storage_bytes_freed: number;
    storage_bytes_net: number;
    asset_keys_added: number;
    asset_keys_removed: number;
    asset_keys_after_commit: number;
    billable_delta_usd_micros: number;
    payment_required: boolean;
    quota_before: {
        storage_bytes: number;
        asset_keys: number;
    };
    quota_after: {
        storage_bytes: number;
        asset_keys: number;
    };
}
export type WarningEntry = LegacyWarningEntry | DeployObservabilityWarningEntry;
export interface LegacyWarningEntry {
    code: string;
    severity: "low" | "medium" | "high";
    requires_confirmation: boolean;
    message: string;
    affected?: string[];
    details?: Record<string, unknown>;
    confidence?: "low" | "medium" | "high";
}
export interface PlanContentRef {
    sha256: string;
    size: number;
    present: boolean;
}
/**
 * Upload session entry for a missing content SHA. Returned by
 * `POST /content/v1/plans` after the deploy plan reports refs as missing.
 * The SDK PUTs bytes to each part's presigned URL; multipart sessions
 * complete via `POST /content/v1/plans/:id/commit`.
 */
export interface MissingContent {
    sha256: string;
    mode: "single" | "multipart";
    parts: Array<{
        part_number: number;
        url: string;
        byte_start: number;
        byte_end: number;
    }>;
    part_size_bytes: number;
    part_count: number;
    upload_id: string;
    staging_key: string;
    expires_at: string;
}
export interface ContentPlanResponse {
    plan_id: string;
    expires_at: string;
    missing: MissingContent[];
    entries: Array<{
        sha256: string;
        missing: boolean;
    }>;
}
export interface PaymentRequiredHint {
    amount: string;
    asset: string;
    payTo: string;
    reason: string;
}
export type ReleaseInventoryStatus = "active" | "superseded" | "failed" | "staged";
export type ReleaseInventoryStateKind = "current_live" | "effective" | "desired_manifest";
export interface SitePathEntry {
    path: string;
    content_sha256: string;
    content_type: string;
}
export interface ReleaseFunctionEntry {
    name: string;
    code_hash: string;
    runtime: string;
    timeout_seconds: number;
    memory_mb: number;
    schedule: string | null;
    triggers?: FunctionTriggerSpec[];
    /** Present when the function's source exports a static `tool` declaration:
     *  the app serves it as an MCP tool at `https://<host>/_run402/mcp`. */
    tool?: ReleaseFunctionTool;
}
/** A function's MCP tool, as release reads report it. Declared in the
 *  function's source as `export const tool = { description, input, title?,
 *  annotations? }` (a literal; the gateway parses it at commit and never runs
 *  the code). A tool call is a POST of the arguments to `route`. */
export interface ReleaseFunctionTool {
    /** The function name, which is the tool name. */
    name: string;
    description: string;
    title?: string;
    /** JSON Schema for the arguments; root `type` is `"object"`. */
    input: Record<string, unknown>;
    annotations?: {
        readOnlyHint?: boolean;
        destructiveHint?: boolean;
        idempotentHint?: boolean;
        openWorldHint?: boolean;
    };
    /** The one exact POST route the tool binds to. */
    route: string | null;
}
/** MCP tools added, removed, or changed between two releases, by name. */
export interface ToolsDiff {
    added: string[];
    removed: string[];
    changed: string[];
}
export interface MigrationAppliedEntry {
    migration_id: string;
    checksum_hex: string;
    applied_at: string;
}
export interface RouteEntry {
    pattern: string;
    kind: "exact" | "prefix";
    prefix: string | null;
    /** Null means all supported route HTTP methods. */
    methods: RouteHttpMethod[] | null;
    target: RouteTarget;
}
export interface MaterializedRoutes {
    manifest_sha256: string | null;
    entries: RouteEntry[];
}
export interface RouteChangeEntry {
    pattern: string;
    before: RouteEntry;
    after: RouteEntry;
    fields_changed: Array<"methods" | "target" | "kind" | "prefix">;
}
export type KnownStaticReachabilityAuthority = "implicit_file_path" | "explicit_public_path" | "route_static_alias";
export type StaticReachabilityAuthority = LiteralUnion<KnownStaticReachabilityAuthority>;
export interface StaticPublicPathInventoryEntry {
    public_path: string;
    asset_path: string;
    reachability_authority: StaticReachabilityAuthority;
    direct: boolean;
    cache_class: StaticCacheClass;
    content_type: string;
    route_id?: string | null;
    methods?: RouteHttpMethod[] | string[] | null;
    [key: string]: unknown;
}
export interface RoutesDiff {
    manifest_sha256_old?: string | null;
    manifest_sha256_new?: string | null;
    added: RouteEntry[];
    removed: RouteEntry[];
    changed: RouteChangeEntry[];
    totals?: {
        added: number;
        removed: number;
        changed: number;
    };
}
export interface ReleaseInventoryBase<StateKind extends ReleaseInventoryStateKind = ReleaseInventoryStateKind> {
    kind: "release_inventory";
    schema_version: "agent-deploy-observability.v1";
    release_id: string | null;
    project_id: string;
    parent_id: string | null;
    status: ReleaseInventoryStatus | null;
    static_continuity?: StaticContinuity;
    static_continuity_paths?: {
        plan_id: string;
        total_count: number;
        paths: string[];
        entries?: Array<{
            public_path: string;
            source_release_id: string;
            source_release_generation: number;
            origin_retention_seconds?: number;
            origin_available_until?: string;
        }>;
        next_cursor: string | null;
    };
    manifest_digest: string | null;
    created_at: string | null;
    created_by: string | null;
    actor: OperationActorSnapshot | null;
    activated_at: string | null;
    superseded_at: string | null;
    operation_id: string | null;
    plan_id: string | null;
    events_url: string | null;
    effective: boolean;
    state_kind: StateKind;
    release_generation: number | null;
    static_manifest_sha256: string | null;
    static_manifest_metadata: StaticManifestMetadata | null;
    site: {
        paths: SitePathEntry[];
        totals?: {
            paths: number;
        };
    };
    static_public_paths?: StaticPublicPathInventoryEntry[];
    /** tenant-site-embedding: the release's framing opt-in as catalog keys, or
     *  `null` (deny). Absent on an older gateway = unknown, not null. */
    embedding?: SiteEmbeddingSpec | null;
    /** site.sign_in_path: the app's own sign-in page for MCP authorization, or
     *  `null` (the hosted `/auth/sign-in`). Absent on an older gateway = unknown. */
    sign_in_path?: string | null;
    /** site.noindex: `true` when every response of the host carries
     *  `X-Robots-Tag: noindex, nofollow, nosnippet`. Absent on an older gateway
     *  = unknown. */
    noindex?: boolean;
    functions: ReleaseFunctionEntry[];
    secrets: {
        keys: string[];
    };
    subdomains: {
        names: string[];
    };
    routes: MaterializedRoutes;
    migrations_applied: MigrationAppliedEntry[];
    /** Capability `routed-locale-context`. The materialized i18n slice on
     *  this release, or `null` when the release has no i18n (clean base or
     *  cleared via `i18n: null`). Gives callers a positive readback after
     *  a deploy — `apply()` not throwing is necessary but not sufficient
     *  verification. Older gateways predating the inventory-i18n change
     *  may omit the field entirely; treat `undefined` as "unknown, fetch
     *  on a newer gateway" rather than as "no slice". */
    i18n?: ReleaseInventoryI18n | null;
    warnings?: DeployObservabilityWarningEntry[];
}
/** Inventory built from the currently live project state. */
export type ActiveReleaseInventory = ReleaseInventoryBase<"current_live">;
/** Inventory for a specific release id. Superseded/active releases are
 *  materialized effective state; staged/failed releases are desired manifests. */
export type ReleaseSnapshotInventory = ReleaseInventoryBase<"effective" | "desired_manifest">;
export type ReleaseInventory = ActiveReleaseInventory | ReleaseSnapshotInventory;
export interface DeployObservabilityWarningEntry {
    code: string;
    severity: "info" | "warn" | "high";
    requires_confirmation: boolean;
    message: string;
    affected?: string[];
    details?: Record<string, unknown>;
    confidence?: "heuristic";
}
export interface PlanMigrationDiff {
    new: Array<{
        id: string;
        checksum_hex: string;
        transaction: "default" | "none";
    }>;
    noop: Array<{
        id: string;
        checksum_hex: string;
    }>;
}
export interface SiteDiff {
    added: Array<{
        path: string;
        sha256: string;
        content_type: string;
    }>;
    removed: string[];
    changed: Array<{
        path: string;
        sha256_old: string;
        sha256_new: string;
        content_type_old: string;
        content_type_new: string;
        content_type_inferred?: true;
    }>;
    totals?: {
        added: number;
        removed: number;
        changed: number;
    };
}
export interface FunctionsDiff {
    added: string[];
    removed: string[];
    changed: Array<{
        name: string;
        fields_changed: Array<"code_hash" | "runtime" | "timeout_seconds" | "memory_mb" | "schedule">;
    }>;
}
/** Secrets have no `changed` bucket; values and value-derived signals are
 *  intentionally absent from deploy observability responses. */
export interface SecretsDiff {
    added: string[];
    removed: string[];
}
/** Subdomains have no `changed` bucket. */
export interface SubdomainsDiff {
    added: string[];
    removed: string[];
}
export interface PlanDiffEnvelope {
    is_noop: boolean;
    summary: string;
    warnings: WarningEntry[];
    migrations: PlanMigrationDiff;
    site: SiteDiff;
    functions: FunctionsDiff;
    secrets: SecretsDiff;
    subdomains: SubdomainsDiff;
    routes: RoutesDiff;
    static_assets: StaticAssetsDiff;
}
export interface ReleaseToReleaseDiff {
    kind: "release_diff";
    schema_version: "agent-deploy-observability.v1";
    from_release_id: string | null;
    to_release_id: string | null;
    is_noop: boolean;
    summary: string;
    warnings: WarningEntry[];
    migrations: {
        applied_between_releases: string[];
    };
    site: SiteDiff;
    functions: FunctionsDiff;
    secrets: SecretsDiff;
    subdomains: SubdomainsDiff;
    routes: RoutesDiff;
    static_assets: StaticAssetsDiff;
    /** MCP tools added, removed, or changed (absent from older gateways). */
    tools?: ToolsDiff;
}
export type ReleaseDiffTarget = "empty" | "active" | (string & {});
export type ReleaseDiffToTarget = "active" | (string & {});
export interface ReleaseInventoryOptions {
    project: string;
    /** Maximum number of site path entries to include. Gateway default: 5,000;
     *  gateway hard maximum: 25,000. */
    siteLimit?: number;
}
export interface ReleaseInventoryByIdOptions extends ReleaseInventoryOptions {
    staticContinuityPlanId?: string;
    staticContinuityCursor?: string;
    releaseId: string;
}
export interface ReleaseDiffOptions {
    project: string;
    from: ReleaseDiffTarget;
    to: ReleaseDiffToTarget;
    /** Maximum number of entries in each site diff bucket. Gateway default:
     *  1,000. */
    limit?: number;
}
/** Server-side summary of the diff between the base release and the new
 *  spec. v1.39+ plans may return the structured `PlanDiffEnvelope`; older
 *  gateways may still return legacy buckets. Migrations mismatch is a hard
 *  deploy error in the modern success path, but the legacy array is kept here
 *  for flag-off/backward compatibility. */
export interface DeployDiff {
    resources?: Record<string, unknown>;
    is_noop?: boolean;
    summary?: string;
    warnings?: WarningEntry[];
    migrations?: PlanMigrationDiff | Array<{
        id: string;
        state: "new" | "noop" | "checksum_mismatch";
    }>;
    site?: SiteDiff;
    functions?: FunctionsDiff;
    secrets?: SecretsDiff;
    routes?: RoutesDiff | Array<{
        kind: "added" | "removed";
        path: string;
    }>;
    static_assets?: StaticAssetsDiff;
    subdomains?: SubdomainsDiff | Array<{
        kind: "added" | "removed";
        subdomain: string;
    }>;
    [key: string]: unknown;
}
export interface DeploySummarySitePaths {
    added: number;
    changed: number;
    removed: number;
    unchanged?: number;
    total_changed: number;
}
export interface DeploySummarySiteCas {
    newly_uploaded_bytes: number;
    reused_bytes: number;
    deployment_copy_bytes_eliminated: number;
}
export interface DeploySummarySite {
    paths?: DeploySummarySitePaths;
    cas?: DeploySummarySiteCas;
}
export interface DeploySummaryFunctions {
    added: string[];
    removed: string[];
    changed: Array<{
        name: string;
        fields_changed: Array<FunctionsDiff["changed"][number]["fields_changed"][number]>;
    }>;
}
export interface DeploySummaryMigrations {
    new: string[];
    noop: string[];
}
export interface DeploySummaryResourceCounts {
    added: number;
    changed: number;
    removed: number;
}
export interface DeploySummaryKeyCounts {
    added: number;
    removed: number;
}
export interface DeploySummaryWarnings {
    count: number;
    blocking: number;
    codes: string[];
}
export interface DeploySummary {
    static_continuity?: StaticContinuity;
    schema_version: "deploy-summary.v1";
    release_id: string;
    operation_id: string;
    is_noop?: boolean;
    headline: string;
    site?: DeploySummarySite;
    functions?: DeploySummaryFunctions;
    migrations?: DeploySummaryMigrations;
    routes?: DeploySummaryResourceCounts;
    secrets?: DeploySummaryKeyCounts;
    subdomains?: DeploySummaryKeyCounts;
    warnings: DeploySummaryWarnings;
}
export type EdgePointerTarget = "kvs" | "cloudfront_invalidation" | "cloudflare_kv";
export type EdgePointerStatus = "pending" | "applied" | "failed" | "not_applicable";
export interface EdgePointerUpdateStatus {
    target: EdgePointerTarget;
    status: EdgePointerStatus;
    attempts?: number;
    last_error?: string | null;
    updated_at?: string | null;
}
export type EdgeBlockState = "converging" | "coherent" | "unknown" | "not_applicable";
export interface EdgeBlock {
    checked_at?: string | null;
    received_at?: string;
    state: EdgeBlockState;
    expected_max_lag_seconds?: number | null;
    pointer_updates: Record<EdgePointerTarget, EdgePointerUpdateStatus | undefined>;
    verify_url?: string | null;
}
export type EdgeProbePathState = "coherent" | "stale_prior_release" | "unknown" | "error";
export type EdgeProbeObservedConfidence = "identity" | "body_hash" | "weak" | "error";
export interface EdgeCoherencePathObservation {
    checked_at?: string;
    verification_basis?: "release_identity" | "content_hash" | "weak_metadata";
    path: string;
    host: string;
    state: EdgeProbePathState;
    observed_confidence: EdgeProbeObservedConfidence;
    expected_release_id: string;
    expected_release_generation: number | null;
    observed_release_id?: string | null;
    observed_release_generation?: number | null;
    expected_sha256?: string | null;
    observed_sha256?: string | null;
    status?: number | null;
    content_type?: string | null;
    content_length?: number | null;
    x_cache?: string | null;
    age_seconds?: number | null;
    error?: string | null;
}
export interface EdgeCoherenceReport {
    checked_at?: string;
    verification_basis?: "no_mutable_paths";
    coherent: boolean;
    operation_id: string;
    project_id: string;
    release_id: string;
    release_generation: number | null;
    paths: EdgeCoherencePathObservation[];
    pending_count: number;
    paths_truncated: boolean;
    path_count: number;
    total_path_count: number;
    vantage: string;
    probe_may_have_warmed_cache: boolean;
    pointer_updates: Record<EdgePointerTarget, EdgePointerUpdateStatus | undefined>;
    next_actions: string[];
    probe_basis?: "no_mutable_paths";
}
export interface EdgeCoherencePollEvent {
    attempts: number;
    elapsedMs: number;
    report: EdgeCoherenceReport;
}
export interface EdgeCoherenceWaitOptions {
    project: string;
    timeoutMs?: number;
    intervalMs?: number;
    onPoll?: (event: EdgeCoherencePollEvent) => void;
}
export interface EdgeCoherenceWaitResult {
    coherent: boolean;
    attempts: number;
    elapsedMs: number;
    report: EdgeCoherenceReport;
}
/** All operation states the gateway exposes. The SDK polls until it reaches
 *  a terminal state. */
export type OperationStatus = "planning" | "uploading" | "committing" | "staging" | "gating" | "migrating" | "schema_settling" | "activating" | "activation_pending" | "needs_repair" | "ready" | "failed" | "rolled_back";
/** Status returned by the synchronous commit response (a subset of
 *  OperationStatus — the commit endpoint never returns mid-phase states like
 *  `gating`/`migrating`; those only appear via subsequent operation polls). */
export type CommitStatus = "running" | "schema_settling" | "activation_pending" | "ready" | "failed";
export interface CommitResponse {
    warnings?: WarningEntry[];
    static_continuity?: StaticContinuity;
    operation_id: string;
    status: CommitStatus;
    release_id?: string;
    urls?: Record<string, string>;
    error?: GatewayDeployError | null;
    edge?: EdgeBlock;
    subdomain_bindings?: SubdomainBindingFreshness[];
    restore_point?: CommitRestorePoint;
    snapshot_skipped_reason?: string;
    actor?: OperationActorSnapshot | null;
    /** Gateway riders on a synchronous `ready` commit: the events-feed `poll`
     *  entry, `watch_errors`, and the `hand_to_member` offer. Passed through
     *  to {@link DeployResult.next_actions} verbatim. */
    next_actions?: NextAction[];
}
export interface CommitRestorePoint {
    snapshot_id: string;
    restore_url: string;
}
export interface OperationSnapshot {
    warnings?: WarningEntry[];
    static_continuity?: StaticContinuity;
    operation_id: string;
    project_id: string;
    plan_id: string;
    status: OperationStatus;
    base_release_id: string | null;
    target_release_id: string | null;
    release_id: string | null;
    urls: Record<string, string> | null;
    payment_required: PaymentRequiredHint | null;
    error: GatewayDeployError | null;
    edge?: EdgeBlock;
    subdomain_bindings?: SubdomainBindingFreshness[];
    activate_attempts: number;
    last_activate_attempt_at: string | null;
    created_at: string;
    updated_at: string;
    rehearsal_report?: ApplyRehearsalReport;
    actor?: OperationActorSnapshot | null;
    /** Present on a `ready` snapshot only: the same gateway riders a synchronous
     *  ready commit carries (`poll`, `watch_errors`, `hand_to_member`). A
     *  polled deploy is never a weaker contract than the commit response. */
    next_actions?: NextAction[];
}
export type RehearsalTeardownPolicy = "keep" | "on_pass" | "always";
export type RehearsalStatus = "passed" | "failed";
export type RehearsalStepStatus = "passed" | "failed" | "skipped";
export interface RehearsePlanOptions {
    teardown?: RehearsalTeardownPolicy;
    project?: string;
}
export interface RehearsalMigrationResult {
    id: string;
    status: RehearsalStepStatus;
    error?: string;
}
export interface RehearsalCheckResult {
    name: string;
    type: "migration" | "static" | "function" | "declared" | (string & {});
    status: RehearsalStepStatus;
    target?: string;
    method?: string;
    expected_status?: number | number[];
    actual_status?: number | null;
    duration_ms: number;
    error?: string;
}
export interface ApplyRehearsalReport {
    kind: "rehearsal_report";
    status: RehearsalStatus;
    operation_id: string;
    source_project_id: string;
    plan_id: string;
    branch_project_id: string | null;
    branch_url: string | null;
    branch_plan_id: string | null;
    branch_operation_id: string | null;
    snapshot_id: string | null;
    started_at: string;
    completed_at: string;
    duration_ms: number;
    migrations: RehearsalMigrationResult[];
    checks: RehearsalCheckResult[];
    teardown: {
        policy: RehearsalTeardownPolicy;
        action: "kept" | "deleted" | "delete_failed" | "skipped" | (string & {});
        error?: string;
    };
    next_actions: Array<{
        type: "commit_plan" | "discard_branch" | "keep_branch" | (string & {});
        command?: string;
        method?: string;
        path?: string;
        url?: string;
        /** `commit_plan` carries the bound commit body: a rehearsed plan commits
         *  only with this `required_plan`. */
        body?: {
            required_plan: {
                plan_id: string;
                plan_fingerprint: string;
            };
        };
        message: string;
    }>;
    error?: GatewayDeployError;
}
export interface RehearsePlanResult {
    operation_id: string;
    status: OperationStatus;
    poll_url: string;
    report: ApplyRehearsalReport;
}
/** Response from `GET /apply/v1/operations`. The gateway may return a
 *  pagination cursor when there are more operations than the requested
 *  page size; clients pass it back as `?cursor=` to fetch the next page. */
export interface DeployListOptions {
    project: string;
    limit?: number;
    cursor?: string;
}
export interface DeployListResponse {
    operations: OperationSnapshot[];
    cursor?: string | null;
}
/** Response from `GET /apply/v1/operations/:operation_id/events`. Returns the
 *  synthesized phase event stream the gateway has recorded so far for the
 *  operation. Same shape as the events emitted by `r.project(id).apply.start().events()`
 *  during an in-flight deploy. */
export interface DeployEventsResponse {
    events: DeployEvent[];
    cursor?: string | null;
}
/** Wire-shape for a structured deploy error from the gateway. The SDK
 *  translates this into `Run402DeployError` for callers. The gateway is
 *  permitted to omit `message` for terse validation errors (e.g. just
 *  `{code: "invalid_spec"}`); the SDK synthesizes a default in that case. */
export interface GatewayDeployError {
    code: string;
    phase?: string | null;
    resource?: string | null;
    message?: string;
    category?: string;
    retryable?: boolean;
    safe_to_retry?: boolean;
    mutation_state?: string;
    trace_id?: string;
    details?: Record<string, unknown> | null;
    next_actions?: unknown[];
    fix?: {
        action: string;
        path?: string;
        [key: string]: unknown;
    } | null;
    logs?: string[] | null;
    rolled_back?: boolean;
    /** Operation id supplied by the gateway when the error is associated with a
     *  specific deploy operation (e.g., MIGRATION_CHECKSUM_MISMATCH). The SDK
     *  prefers this over the caller-supplied operation id when constructing
     *  `Run402DeployError`, so resume hints round-trip correctly. */
    operation_id?: string;
    /** Plan id supplied by the gateway when the error originates inside a plan
     *  context. Same precedence rule as `operation_id`. */
    plan_id?: string;
    [key: string]: unknown;
}
/** What the SDK actually POSTs to `/apply/v1/plans`. Bytes are content
 *  refs, not inline. The gateway's wire envelope is `{ spec, manifest_ref?,
 *  idempotency_key? }`. Most callers never construct this directly; it's
 *  produced by the SDK's normalizer and exposed for the low-level
 *  `apply.plan` layer. */
export interface PlanRequest {
    spec: Record<string, unknown>;
    manifest_ref?: Record<string, unknown>;
    idempotency_key?: string;
    mode?: "reviewed_plan";
    required_plan?: {
        plan_id: string;
        plan_fingerprint?: string;
    };
    /** vault (protocol §6.5): declares the capture this plan is bound to. */
    vault?: VaultPlanDeclaration;
}
/**
 * The capture declaration a vault-capable client sends at plan time. The
 * gateway answers with the canonical `apply_plan_sha256` the activation token
 * is minted against; without this block the plan carries no capture and a
 * `vault_policy: required` project refuses its commit.
 */
export interface VaultPlanDeclaration {
    capture_id: string;
    snapshot_oid_hmac: string;
}
/** The vault block on a commit: an activation token, or an audited override. */
export type VaultCommitDeclaration = {
    activation_token_id: string;
} | {
    allow_unvaulted: true;
    override_reason: string;
};
export interface NormalizedReleaseSpec {
    project_id: string;
    base?: ReleaseSpec["base"];
    database?: NormalizedDatabaseSpec;
    secrets?: SecretsSpec;
    functions?: NormalizedFunctionsSpec;
    site?: NormalizedSiteSpec;
    subdomains?: SubdomainsSpec;
    routes?: ReleaseRoutesSpec;
    checks?: SmokeCheck[];
    /** v1.48 unified-apply: post-normalization asset slice. `put` entries
     *  are wire-shaped `AssetPutEntry[]` (no `source` field — the SDK
     *  normalizer stripped them after registering byte-readers). */
    assets?: NormalizedAssetSpec;
    /** v2.5 routed-locale-context: same shape as `ReleaseSpec.i18n`. Passed
     *  through unchanged from caller to wire so the gateway can plan the
     *  slice and downstream warnings/diffs can reference it. */
    i18n?: I18nSpec | null;
}
export interface NormalizedAssetSpec {
    put?: AssetPutEntry[];
    delete?: string[];
    sync?: {
        prefix: string;
        prune: true;
        confirm?: AssetSyncPruneConfirm;
    };
}
export interface NormalizedDatabaseSpec {
    migrations?: NormalizedMigrationSpec[];
    expose?: ExposeManifest;
    zero_downtime?: boolean;
}
export interface NormalizedMigrationSpec {
    id: string;
    /** Lowercase hex SHA-256 of the migration SQL. Required by the gateway. */
    checksum: string;
    /** Cloud wire form: migration SQL staged in CAS and referenced by digest. */
    sql_ref?: ContentRef;
    /** Run402 Core wire form: migration SQL carried inline. */
    sql?: string;
    transaction?: "required" | "none";
}
export interface NormalizedFunctionsSpec {
    replace?: Record<string, NormalizedFunctionSpec>;
    patch?: {
        set?: Record<string, NormalizedFunctionSpec>;
        delete?: string[];
    };
}
export interface NormalizedFunctionSpec {
    runtime?: "node22";
    source?: ContentRef;
    files?: Record<string, ContentRef>;
    entrypoint?: string;
    config?: {
        timeoutSeconds?: number;
        memoryMb?: number;
    };
    /** Capability `apply-v1-function-deps` — see `FunctionSpec.deps`. */
    deps?: string[];
    triggers?: FunctionTriggerSpec[];
    schedule?: string | null;
    /** v1.51+ — see `FunctionSpec.requireAuth`. */
    requireAuth?: boolean;
    /** v1.51+ — see `FunctionSpec.requireRole`. */
    requireRole?: RequireRoleSpec | null;
    /** v1.52+ — see `FunctionSpec.class`. */
    class?: "ssr" | "standard";
    /** v1.52+ — see `FunctionSpec.capabilities`. */
    capabilities?: string[];
}
export type NormalizedSiteSpec = {
    replace: Record<string, ContentRef>;
    patch?: never;
    public_paths?: SitePublicPathsSpec;
    embedding?: SiteEmbeddingSpec | null;
    sign_in_path?: string | null;
    noindex?: boolean | null;
} | {
    patch: {
        put?: Record<string, ContentRef>;
        delete?: string[];
    };
    replace?: never;
    public_paths?: SitePublicPathsSpec;
    embedding?: SiteEmbeddingSpec | null;
    sign_in_path?: string | null;
    noindex?: boolean | null;
} | {
    public_paths: SitePublicPathsSpec;
    replace?: never;
    patch?: never;
    embedding?: SiteEmbeddingSpec | null;
    sign_in_path?: string | null;
    noindex?: boolean | null;
} | {
    embedding: SiteEmbeddingSpec | null;
    replace?: never;
    patch?: never;
    public_paths?: never;
    sign_in_path?: string | null;
    noindex?: boolean | null;
} | {
    sign_in_path: string | null;
    replace?: never;
    patch?: never;
    public_paths?: never;
    embedding?: never;
    noindex?: boolean | null;
} | {
    noindex: boolean | null;
    replace?: never;
    patch?: never;
    public_paths?: never;
    embedding?: never;
    sign_in_path?: never;
};
export type DeployEvent = {
    type: "plan.started";
} | {
    type: "rehearsal.started";
    planId: string;
} | {
    type: "rehearsal.finished";
    planId: string;
    status: "passed" | "failed";
    operationId: string;
    branchProjectId: string | null;
    durationMs: number;
} | {
    type: "rehearsal.skipped";
    reason: NonNullable<DeployRehearsalBlock["reason"]>;
} | {
    type: "plan.diff";
    diff: DeployDiff;
} | {
    type: "plan.warnings";
    warnings: WarningEntry[];
} | {
    type: "payment.required";
    amount: string;
    asset: string;
    payTo: string;
    reason: string;
} | {
    type: "payment.paid";
    tx?: string;
} | {
    type: "deploy.retry";
    attempt: number;
    nextAttempt: number;
    maxAttempts: number;
    delayMs: number;
    code: string;
    phase: string | null;
    resource: string | null;
    operationId: string | null;
    planId: string | null;
    message: string;
} | {
    type: "content.upload.skipped";
    label: string;
    sha256: string;
    reason: "present" | "satisfied_by_plan";
    /** Which spec slice(s) contributed this SHA. `"release"` for
     *  database / functions / site / migrations; `"asset"` for the v1.48
     *  unified-apply asset slice; `"mixed"` when the same SHA was
     *  registered by both kinds (cross-kind CAS dedup). */
    slice_kind?: "release" | "asset" | "mixed";
} | {
    type: "content.upload.progress";
    label: string;
    sha256: string;
    done: number;
    total: number;
    slice_kind?: "release" | "asset" | "mixed";
} | {
    type: "commit.phase";
    phase: "validate" | "stage" | "migrate-gate" | "migrate" | "schema-settle" | "activate" | "ready";
    status: "started" | "done" | "failed";
    /** Sorted list of slice kinds the apply's spec carried. Useful for
     *  agents grouping per-phase telemetry by slice category. Stable
     *  across phase transitions of the same apply. */
    slice_kinds?: ("release" | "asset")[];
} | {
    type: "commit.phase.detail";
    id?: string;
    operation_id?: string;
    project_id?: string;
    phase: string;
    status: "started" | "done" | "failed" | "skipped" | "deferred";
    message: string | null;
    details: Record<string, unknown>;
    created_at?: string;
    updated_at?: string;
} | {
    type: "log";
    resource: string;
    stream: "stdout" | "stderr";
    line: string;
} | {
    type: "ready";
    releaseId: string;
    urls: Record<string, string>;
    slice_kinds?: ("release" | "asset")[];
};
export interface DeployResult {
    static_continuity?: StaticContinuity;
    release_id: string;
    operation_id: string;
    urls: Record<string, string>;
    /** How rehearsal was handled: `passed` (rehearsed on a branch, then
     *  committed), or `skipped` with a `reason`. A failed rehearsal never
     *  reaches a result — `apply()` throws `REHEARSAL_FAILED` carrying the
     *  report. Cloud only; absent on Core. */
    rehearsal?: DeployRehearsalBlock;
    /** Public-edge coherence/convergence hint returned by the gateway. When
     *  `state` is `converging`, call `p.apply.edgeCoherence(operationId)` or
     *  `p.apply.waitEdgeCoherent(operationId)` before declaring mutable public
     *  URLs freshly visible everywhere. */
    edge?: EdgeBlock;
    /** The `diff` from the plan response — useful for "what changed in this
     *  deploy" UX. */
    diff: DeployDiff;
    /** Structured plan warnings that were observed before commit. */
    warnings: WarningEntry[];
    /**
     * Advisory follow-ups — never a gateway plan warning (those stay in
     * {@link warnings}). Carries the gateway's own riders from a synchronous
     * `ready` commit verbatim (`poll` positioned at this deploy's activation
     * event, `watch_errors`, and `hand_to_member` — the offer to hand your
     * human the site and console links and relay Run402's free promotion),
     * plus the one shape the deploy itself synthesizes:
     * `vault_policy_required`, offered on every deploy of a vaulted project
     * whose `vault_policy` was never set (repo-first-onramp D3). Absent when
     * there is nothing to offer. A commit that went asynchronous and was polled to
     * `ready` carries the same gateway riders from the ready snapshot.
     */
    next_actions?: NextAction[];
    /** Freshness hints for stable hosts affected by this deploy. Managed
     *  subdomains and custom domains are eventually consistent at the edge, so
     *  app installers can distinguish fresh propagation misses from real verify
     *  failures. */
    subdomain_bindings?: SubdomainBindingFreshness[];
    restore_point?: CommitRestorePoint;
    snapshot_skipped_reason?: string;
    /** v1.48 unified-apply: present when the spec carried an `assets` slice.
     *  Built from the plan response's `asset_entries[].asset_ref` (gateway-
     *  authoritative URLs) plus the realised `content.upload.*` event stream
     *  for `totals.bytes_uploaded` / `bytes_reused`. `undefined` for release-
     *  only applies. */
    assets?: AssetManifest;
}
/**
 * Batch result envelope for the assets slice. Mirrors the @run402/sdk/node
 * `AssetManifest` shape exposed from the Node entry; defined on the
 * isomorphic side so consumers reading `DeployResult.assets` get a typed
 * shape without importing the Node entry.
 *
 * `byKey` and `manifest` MUST be constructed with `Object.create(null)`
 * so attacker-controlled or filesystem-derived keys like
 * `__proto__` / `constructor` / `toString` don't collide with prototype
 * properties.
 */
export interface AssetManifest {
    /** Iteration order = submission order. */
    list: AssetManifestEntry[];
    /** Null-prototype object indexed by asset key. */
    byKey: Record<string, AssetManifestEntry>;
    /** Null-prototype object, JSON-serializable, suitable for writing to
     *  disk as a `asset-manifest.json` artifact. */
    manifest: Record<string, AssetManifestEntry>;
    totals: {
        files: number;
        bytes_uploaded: number;
        bytes_reused: number;
        duration_ms: number;
    };
    /** Populated when the apply carried `assets.sync.prune: true`. */
    pruned?: string[];
}
export interface AssetManifestEntry {
    key: string;
    sha256: string;
    size_bytes: number;
    content_type: string;
    visibility: "public" | "private";
    /** Mutable public URL (alias for the deprecated `url` field). */
    url: string | null;
    /** Content-hashed immutable URL. Null when the entry is private or
     *  `immutable: false`. */
    immutable_url: string | null;
    /** CDN-form of the mutable URL (auto-subdomain). Alias maintained for
     *  parity with single-asset `AssetRef`. */
    cdn_url: string | null;
    /** CDN-form of the immutable URL. The recommended URL for generated
     *  HTML/CSS/JS — pair with `sri`. */
    cdn_immutable_url: string | null;
    /** Browser SRI form: `sha256-<base64>`. Null on non-immutable / private
     *  uploads. */
    sri: string | null;
    /** Strong ETag `"sha256-<hex>"`. */
    etag: string | null;
    /** RFC 9530 `Content-Digest` value `sha-256=:<base64>:`. */
    content_digest: string | null;
    width_px?: number;
    height_px?: number;
    blurhash?: string;
    variant_spec_version?: string;
    display_url?: string;
    display_immutable_url?: string;
    variants?: {
        thumb?: ResolvedAssetVariant;
        medium?: ResolvedAssetVariant;
        large?: ResolvedAssetVariant;
        display_jpeg?: ResolvedAssetVariant;
    };
    metadata?: Record<string, string | number | boolean | string[]> | null;
    image_format?: string | null;
    image_info?: Record<string, unknown> | null;
    image_exif?: Record<string, unknown> | null;
    image_exif_policy?: "keep" | "strip" | null;
    blurhash_data_url?: string | null;
    asset_schema?: "v1.49" | "v1.50" | "v1.54" | null;
}
export interface ApplyOptions {
    /** Synchronous progress callback. Throws inside the callback are caught
     *  and silently dropped — a buggy consumer cannot abort a deploy. */
    onEvent?: (event: DeployEvent) => void;
    /** Client-side idempotency key. The SDK passes this to the gateway, which
     *  combines it with the manifest digest to deduplicate retries. Default:
     *  the gateway-computed manifest digest itself acts as the key. */
    idempotencyKey?: string;
    /** Continue past plan warnings that require confirmation. Default false:
     *  `apply()` aborts before upload/commit so agents can set missing secrets,
     *  inspect warnings, or use the low-level plan/upload/commit flow. */
    allowWarnings?: boolean;
    /** Continue past specific confirmation-required warning codes. Every
     *  blocking warning must be covered by this list or by `allowWarnings`. */
    allowWarningCodes?: string[];
    /** Skip the automatic rehearsal `apply()` runs for a migration-bearing plan
     *  against a project with a live release. Default false: when the plan's
     *  `rehearsal.available` is true the plan is rehearsed on a contained
     *  branch and committed only on a passing report. */
    noRehearse?: boolean;
    /** Bind this apply to a reviewed plan returned by `plan(..., { mode: "reviewedPlan" })`. */
    requiredPlan?: {
        planId: string;
        planFingerprint?: string;
    };
    /** Automatic safe-race retries after the initial `apply()` attempt.
     *  Default: 2 retries (3 total attempts). Pass 0 to disable automatic
     *  retry and surface the first safe deploy race to the caller. */
    maxRetries?: number;
    /** Runtime target. CLI sets this automatically from `run402 init --api-base`.
     *  Cloud keeps CAS content plans and operation polling; Core uses the
     *  self-hosted gateway's direct content staging and immediate commit result. */
    target?: "cloud" | "core";
    /** vault §6.5 — supplied by `applyWithVault`, never by hand. */
    vault?: VaultApplyHooks;
}
/**
 * The vault handshake `applyWithVault` (`@run402/sdk/node`) injects into
 * `apply()`. The declaration rides the plan; `authorize` is called once the
 * plan exists and its content is uploaded, and returns the block the commit
 * presents. Throwing from `authorize` aborts the apply with NOTHING committed
 * — which is how `SNAPSHOT_MOVED_DURING_DEPLOY` stops a deploy.
 *
 * An apply carrying these hooks does not auto-retry: a retry would re-plan
 * under a NEW operation, and an activation token is minted for exactly one.
 */
export interface VaultApplyHooks {
    declaration: VaultPlanDeclaration;
    authorize(planned: {
        plan_id: string;
        operation_id: string;
        apply_plan_sha256: string | null;
    }): Promise<VaultCommitDeclaration>;
}
export interface StartOptions {
    idempotencyKey?: string;
    /** Receives progress events for plan diff, content uploads, commit phases,
     *  warnings, payment requirements, and final release activation. Callback
     *  exceptions are swallowed so UI/logging hooks cannot fail the deploy. */
    onEvent?: (event: DeployEvent) => void;
    /** By default, warnings with `requires_confirmation` stop before
     *  upload/commit. Set true only after inspecting the warnings. */
    allowWarnings?: boolean;
    /** Continue past specific confirmation-required warning codes. */
    allowWarningCodes?: string[];
    requiredPlan?: {
        planId: string;
        planFingerprint?: string;
    };
}
/**
 * Options for the `r.project(id).apply.promote(releaseId, opts?)`
 * pointer-swap operation. Mirrors `ApplyOptions` for the parts that apply
 * (`allowWarnings`, `allowWarningCodes`); skips the parts that don't
 * (`onEvent` — promote is a single-shot operation, no per-phase events;
 * `idempotencyKey` — gateway derives idempotency from `(project, release_id)`;
 * `maxRetries` — no plan-time race window).
 */
export interface PromoteOptions {
    /** Continue past confirmation-required warnings (e.g. MIGRATIONS_NOT_REVERSIBLE).
     *  Default false: the gateway aborts before the pointer swap when a
     *  blocking warning isn't covered. */
    allowWarnings?: boolean;
    /** Cover specific confirmation-required warning codes. Every blocking
     *  warning must be covered by this list or by `allowWarnings`. */
    allowWarningCodes?: string[];
}
/**
 * Result envelope returned by `r.project(id).apply.promote(releaseId, opts?)`.
 * The promote operation is a single-shot pointer swap; no phase events,
 * no payment-required hook (promote uses existing-release content).
 */
export interface PromoteResult {
    static_continuity?: StaticContinuity;
    status: "ok";
    /** The release id now live on the project. Equal to the input releaseId. */
    release_id: string;
    /** The new `internal.apply_operations` row id (created with kind='promote'). */
    operation_id: string;
    /** The release that was live BEFORE the swap. */
    previous_release_id: string;
    /** Structured diff between previous and new live release. */
    diff: PromoteDiff;
    /** Any structured warnings produced — including ones the caller acked. */
    warnings: WarningEntry[];
    /**
     * Functions re-deployed to the target release's code after the pointer
     * swap (functions are not versioned per release). `failed` entries keep the
     * code they had and also appear as a `FUNCTION_REDEPLOY_FAILED` warning.
     * Absent from gateways that predate function rollback.
     */
    functions?: FunctionRollbackOutcome;
}
export interface FunctionRollbackOutcome {
    redeployed: string[];
    failed: Array<{
        name: string;
        code: "FUNCTION_SOURCE_NOT_FOUND" | "FUNCTION_REDEPLOY_FAILED" | (string & {});
        message: string;
    }>;
}
export interface PromoteDiff {
    functions: {
        only_in_current: string[];
        only_in_target: string[];
        changed: string[];
    };
    migrations: {
        only_in_current: string[];
        only_in_target: string[];
    };
    site_paths: {
        added_in_current: number;
        removed_in_current: number;
    };
}
export interface DeployOperation {
    /** The operation id (also exposed via the snapshot). */
    readonly id: string;
    /** Async iterable of events for as long as the operation is non-terminal. */
    events(): AsyncIterable<DeployEvent>;
    /** Resolves with the final result, or rejects with `Run402DeployError`. */
    result(): Promise<DeployResult>;
    /** Latest snapshot from the gateway. */
    snapshot(): Promise<OperationSnapshot>;
}
/** Automatic origin continuity for previously public non-HTML paths. */
export interface StaticContinuity {
    mode: 'absent_public_paths';
    origin_retention_seconds: 3600;
    scope: 'previously_public_non_html';
    source_release_id: string | null;
    source_release_generation: number | null;
    retained_path_count: number;
    /** Omitted for plans; null when this release has no retired origin deadline. */
    origin_available_until?: string | null;
}
export {};
//# sourceMappingURL=deploy.types.d.ts.map