import type { ReadonlyJSONObject } from "assistant-stream/utils";
import type { AssistantCloud } from "./AssistantCloud.js";
import type { CloudMessage, CloudMessageRun } from "./AssistantCloudThreadMessages.js";
import type { CloudRunEndReason } from "./AssistantCloudThreadRuns.js";
/** How a write relates to the message's run: `running` opens or renews its lease, a reason ends it. */
export type CloudMessageRunPhase = "running" | CloudRunEndReason;
/** How long a running lease may go without a heartbeat before its message reads as stale. */
export declare const DEFAULT_RUN_STALE_AFTER_MS = 30000;
export type CloudMessagePersistenceOptions = {
    /**
     * Milliseconds without a heartbeat after which a running lease reads as
     * stale, which is what a run whose tab went away looks like. Default
     * 30 000. The server never expires a run: a late run that beats again
     * reads as running again, and its writes still land.
     */
    staleAfter?: number | undefined;
};
/** Where a listed message's run stands for a reader: still beating, or quiet for longer than `staleAfter`. */
export type CloudRunState = {
    status: "in-progress";
    staleInMs: number;
} | {
    status: "stale";
};
/**
 * Appends, updates and loads cloud messages while mapping local ids to cloud
 * ids and chaining parent_id. A parent that is still being created is awaited,
 * so concurrent appends land under the right parent. A parent whose create
 * failed is created again before its child, and a child whose parent still
 * cannot be created is stored as a root.
 *
 * A message appended as running holds a run lease, renewed by heartbeats on
 * the server's interval until a write with an end reason ends it. Heartbeats
 * keep running while the page is in the background. The server never
 * expires a lease; readers show a run as stale once it has gone `staleAfter`
 * without a beat, and a late run keeps writing its own message. A lease that
 * is lost (ended elsewhere or refused) is final: the message gets no further
 * writes from this persistence.
 */
export declare class CloudMessagePersistence {
    private idMapping;
    private failedAppends;
    /** Every message this persistence opened a lease for; a repeated append of one writes its row instead of storing another. */
    private runs;
    private runWrites;
    /** Writes waiting on an in-flight create, chained so they apply in append order. */
    private pendingWrites;
    /** The deletes each write waiting on an in-flight create saw start; each resolves to whether it deleted the message. */
    private waiters;
    private getCloud;
    private staleAfter;
    constructor(cloud: AssistantCloud, options?: CloudMessagePersistenceOptions);
    constructor(getCloud: () => AssistantCloud, options?: CloudMessagePersistenceOptions);
    /**
     * Reads a listed message's run against `staleAfter`; `sinceReadMs` is the
     * time since the list was read. Undefined for no run or an ended one.
     */
    runStateOf(run: CloudMessageRun | undefined, sinceReadMs?: number): CloudRunState | undefined;
    /**
     * Persist a message to the cloud.
     *
     * @param threadId - Remote thread ID
     * @param messageId - Local message ID (used for tracking)
     * @param parentId - Local parent message ID (or null for first message)
     * @param format - Message format (e.g., "aui/v0", "ai-sdk/v6")
     * @param content - Message content (format-specific)
     * @param run - `running` stores the message with an open run lease that is
     *   kept alive by heartbeats until a write with an end reason closes it.
     *   Once the lease is lost (it was ended elsewhere or the server refused
     *   it), later writes for the message are dropped.
     */
    append(threadId: string, messageId: string, parentId: string | null, format: string, content: ReadonlyJSONObject, run?: CloudMessageRunPhase): Promise<void>;
    private resolveParentId;
    /**
     * Update an already-persisted message in the cloud.
     */
    update(threadId: string, messageId: string, _format: string, content: ReadonlyJSONObject, run?: CloudMessageRunPhase): Promise<void>;
    private writeRun;
    /**
     * Moves a stored message's id mapping and run lease to a new local id, for
     * runtimes that swap a placeholder's id once the backend names the message.
     */
    rename(from: string, to: string): Promise<void>;
    /** Whether a run lease this persistence opened for the message is still held. */
    hasOpenRun(messageId: string): boolean;
    private openRun;
    private scheduleHeartbeat;
    private heartbeat;
    /** The lease is gone for good: stop heartbeating and drop every later write for the message. */
    private loseRun;
    private closeRun;
    /**
     * Delete mapped messages from the cloud and clear their local ID mappings.
     */
    delete(threadId: string, messageIds: string[]): Promise<void>;
    private deleteMessages;
    /**
     * Check if a message has been persisted (or is currently being persisted).
     */
    isPersisted(messageId: string): boolean;
    /**
     * Get the remote ID for a local message ID (resolved).
     * Returns undefined if not persisted.
     */
    getRemoteId(messageId: string): Promise<string | undefined>;
    getResolvedRemoteId(messageId: string): string | undefined;
    record(localId: string, remoteId: string): void;
    /**
     * Load messages from the cloud and populate the ID mapping.
     *
     * The list endpoint caps a response at 200 rows, so pages are followed by
     * message ID cursor until a short page and concatenated in server order.
     *
     * The ID mapping is populated so that `isPersisted()` returns true for
     * loaded messages, preventing re-persistence of already-stored messages.
     *
     * A loaded ID that an append already maps keeps the remote ID from that append, and falls back to the loaded ID if the append fails.
     *
     * @param threadId - Remote thread ID
     * @param format - Optional format filter
     * @returns Array of cloud messages
     */
    load(threadId: string, format?: string): Promise<CloudMessage[]>;
    /**
     * Reset the ID mapping (call when switching threads).
     *
     * Pending `load()` and `append()` calls are not cancelled and still settle
     * normally, but their results no longer populate the ID mapping.
     */
    reset(): void;
}
//# sourceMappingURL=CloudMessagePersistence.d.ts.map