import { type Json, type Provider, type Target } from "./protocol.js";
export type DeliveryOutcome = "delivered" | "pending";
/** What a phone message is known to have become. `queued`: typed into the
 * pane and held by the agent until its turn ends. `failed` carries a reason
 * and may still turn `delivered` if the agent takes it late. */
export type DeliveryState = "queued" | "delivered" | "failed";
/** A transcript row carrying a message's words: its byte offset, and whether the agent fires UserPromptSubmit for it (a user row does; a mid-turn queue removal does not). */
export interface TranscriptEntry {
    offset: number;
    hook: boolean;
}
export type TrackedState = DeliveryState | "unknown";
export interface DeliveryStatus {
    state: TrackedState;
    reason?: string;
    session?: string;
}
/** A message is kept this long, by its id, so a late status or a retry still
 * finds it: a busy Codex turn can run far past the old ten minutes. */
export declare const DELIVERY_RETENTION_MS: number;
/** An agent that ended its turn after the message was typed and then stayed
 * idle this long without taking it has dropped it. */
export declare const DELIVERY_GIVE_UP_MS = 30000;
/** What the agent hands its UserPromptSubmit hook is the terminal's pasted
 * form of what Phren typed; compare the words, not the wrapping. Claude Code
 * also takes each attached picture's path line out of the text and puts an
 * "[Image #N]" label at the front, so neither side keeps picture paths or
 * labels. */
export declare function promptKey(text: string): string;
export declare const promptHash: (text: string) => string;
/** One message per record, bound to the pane it was typed into (server, pane,
 * agent kind), not to the conversation: a Codex thread or a Claude /clear can
 * replace the conversation while the agent still holds the typed text, and
 * the conversation that submits it is the one it reached. Records hold a hash
 * of the words, never the text, and those with a phone id are written to
 * `file` so a Hook restart keeps their state. */
export declare class PromptDeliveries {
    private file?;
    private now;
    private landed;
    private records;
    private waiters;
    private writing;
    /** Panes (server, pane, agent) that sent a UserPromptSubmit or Stop since the Hook started. */
    private hookPanes;
    private paneKey;
    private static open;
    /** Open, or failed with its own hook's claim still waiting for its row (a late delivery). */
    private static claimable;
    constructor(file?: string | undefined, now?: () => number, landed?: (record: {
        source: Provider;
        session: string;
        hash: string;
        at: number;
    }) => Promise<TranscriptEntry[]>);
    private samePane;
    private byId;
    private prune;
    private save;
    /** Settled writes, for tests and an orderly shutdown. */
    flush(): Promise<void>;
    /** Register a message about to be typed into `target`'s pane. Settles
     * "delivered" once a conversation in that pane submits the words, or
     * "pending" after `waitMs`; the record outlives the wait. A retry of a
     * failed id replaces its record. Aborting (the terminal refused before
     * writing) drops it. */
    expect(target: Target, text: string, waitMs: number, signal?: AbortSignal, id?: string, terminal?: string): Promise<DeliveryOutcome>;
    private release;
    private open;
    /** A message Phren typed into `target`'s pane that no conversation there has submitted yet. */
    pending(target: Target, text: string): boolean;
    /** Wait again for a message `expect` reported pending, after a second Enter. */
    awaitLate(target: Target, text: string, waitMs?: number): Promise<DeliveryOutcome>;
    private find;
    /** The Hook answered `id` as typed and held by the agent. */
    queue(id: string, target: Target): void;
    /** Delivered without a hook: an app-server turn or a served OpenCode prompt. */
    delivered(id: string | undefined, target: Target, text: string): void;
    /** The message did not reach the agent, and why. It stays matchable, so a
     * late submission still turns it delivered. */
    fail(id: string | undefined, target: Target, reason: string): void;
    /** The pane's Codex server moved to another thread (/new, /resume):
     * input still pending on the old one stays there, out of the pane's view.
     * Fails loudly; a late UserPromptSubmit from the old thread still settles it. */
    threadChanged(previous: Target): void;
    /** False when `id` names another message: other words or another pane. */
    sameMessage(id: string, target: Pick<Target, "server" | "pane" | "source">, text: string): boolean;
    /** What became of phone message `id` in `target`'s pane. `unknown` while the
     * Hook has not answered it, for another pane, or past retention. */
    status(id: string, target: Pick<Target, "server" | "pane" | "source">): DeliveryStatus;
    /** Every known phone message in `target`'s pane, for its conversation's stream. */
    forPane(target: Pick<Target, "server" | "pane" | "source">): ({
        deliveryId: string;
    } & DeliveryStatus)[];
    /** Conversation `target` just submitted `prompt`. Its own record wins, then
     * the oldest open one in the same pane: the agent kept the text across a
     * conversation change, and the conversation now in the pane is where it
     * went. A prompt nothing matches was typed locally. Never refused.
     * For Claude the transcript is consulted first and the submission claims
     * the earliest unclaimed user row with these words, through the same
     * `settle` and the same table of claimed entries the idle check uses: a
     * callback for a row already claimed (the idle check settled it, the hook
     * came late) settles nothing more. */
    submitted(target: Target, prompt: string): Promise<void>;
    /** Marks `record` delivered, with the transcript entry that settled it when known. */
    private settle;
    /** Whether the transcript shows the agent took `record`'s message, claiming the entry it settled with.
     * An entry settles one message: the table of records' `entry` offsets (per conversation) is the only
     * state, so neither this path nor the hook's can settle two messages with one turn. A user row is a
     * turn start whose UserPromptSubmit hook settles the message itself, so while the agent works only a
     * mid-turn queue removal (no hook fires for it) settles from the transcript; once the agent is idle
     * (`working` false) a user row settles too, for a hook that never came. Of several identical open
     * messages the oldest take the entries. `pass` carries the entries still unclaimed within one observe. */
    private taken;
    /** A turn in `target`'s pane ended: the agent now takes held input or never will. */
    stopped(target: Target): void;
    /** Called on the Hook's activity tick with a server's panes. A message
     * still held fails loudly when its pane closed, its agent restarted, or the
     * agent ended a turn and stayed idle without taking it (unless its
     * transcript shows it after all). */
    observe(server: string, panes: Json[]): Promise<void>;
}
/** The byte offsets of the user rows with these words that reached the conversation's transcript
 * after the message was typed: the agent took it without its hook saying so
 * (a mid-turn steer). Reads at most the last 512 KiB. */
/** The byte offsets (in the file, `start` being the buffer's own offset) of the rows in `buffer`
 * whose user text has `record`'s words, newest first. One offset per row. */
export declare function matchingOffsets(buffer: Buffer, start: number, record: {
    hash: string;
    at: number;
}): TranscriptEntry[];
/** A transcript row's user-typed text: Claude's user rows, Codex's
 * user_message events and user response items. */
export declare function userTexts(row: Json): string[];
