import type { BridgeEvent, BridgeHello, BridgeQuestion } from './bridge-endpoints.js';
/**
 * An answer picked in the dashboard, on its way back to the session (#1237).
 *
 * `queued` until the extension fetches it, delivers it into the composer and acknowledges;
 * then `sent`, or `failed` with the extension's reason. The id is what the acknowledgement
 * references, so a stale ack from a tab that died mid-delivery cannot resolve a newer answer.
 */
export interface BridgeAnswer {
    id: string;
    sessionId: string;
    label: string;
    queuedAt: string;
    state: 'queued' | 'sent' | 'failed';
    note?: string;
}
/**
 * The questions a Claude web session is parked on, keyed by its cloud session id (#1237).
 *
 * A cloud agent has no live local session to hang a choice gate on: #1231 ends the agent at the
 * hand-off, so by the time the agent asks anything the agent is already `done`. The join back to
 * an agent is `AgentMeta.sessionId`, which a web agent already carries because `CloudSession` reports
 * the cloud id on its result.
 *
 * In memory on purpose. A question is only answerable while the session that asked it is still
 * parked, and the bridge that reported it re-reports on reconnect, so surviving a daemon restart
 * would preserve a question that may already have been answered elsewhere.
 */
/** The last time anything spoke to the bridge, and how it went. */
export interface BridgeContact {
    at: string;
    route: string;
    status: number;
}
/** The extension's last version claim, and whether the doorway turned it away (#1519). */
export interface BridgeVersion {
    got: string;
    expected: string;
    blocked: boolean;
    at: string;
}
export declare class BridgeQuestions {
    private readonly bySession;
    private contact;
    /**
     * Note that something reached the bridge, whatever the outcome.
     *
     * Failures are recorded too, and that is the point: an extension that is misconfigured looks
     * exactly like one that is not installed, because both leave no question behind. A refused
     * request at least proves something is trying.
     */
    recordContact(route: string, status: number): void;
    private versionState;
    /**
     * What version the extension last claimed and whether it was refused for it (#1519).
     *
     * The accepted claims are recorded too, which is what clears a blocked banner: the moment an
     * updated extension gets through, the last word is no longer a refusal.
     */
    recordVersion(got: string, expected: string, blocked: boolean): void;
    /** The last version claim, or undefined while nothing has stated one. */
    version(): BridgeVersion | undefined;
    private helloState;
    /** What the injected page script last said about itself. */
    recordHello(hello: BridgeHello): void;
    /** The page script's last report, or undefined if none has ever arrived. */
    hello(): BridgeHello | undefined;
    /** The last contact, or undefined if nothing has ever reached the bridge. */
    lastContact(): BridgeContact | undefined;
    private readonly eventsBySession;
    /**
     * Record one transcript entry, keyed by its position.
     *
     * Keyed rather than appended because the page is re-read on every DOM change, so the same
     * message arrives repeatedly and a growing list would be mostly duplicates. Position also lets
     * a later read replace an earlier one, which is what a message still being streamed needs.
     */
    recordEvent(event: BridgeEvent): void;
    /** That session's transcript so far, in order. */
    events(sessionId: string): BridgeEvent[];
    private readonly answersBySession;
    /** The fingerprint of the question each session's `sent` answer resolved. */
    private readonly answeredBySession;
    /**
     * Record the question a session is parked on, replacing any earlier one for that session.
     *
     * A question identical to one an answer was already delivered for is dropped: the extension's
     * worker forgets what it sent when it restarts, and the answered block stays in the page's DOM,
     * so the same question would otherwise resurface as parked right after being answered.
     */
    record(question: BridgeQuestion): void;
    /**
     * Queue an answer picked in the dashboard (#1237). Refuses anything but a label of the
     * question currently parked, so the only text this can ever put in a composer is one the
     * session itself offered.
     */
    queueAnswer(sessionId: string, label: string): BridgeAnswer | string;
    /** Withdraw a queued answer. Too late once the extension has delivered it. */
    cancelAnswer(sessionId: string): boolean;
    /** The answer waiting for the extension to deliver, if any. */
    pendingAnswer(sessionId: string): BridgeAnswer | undefined;
    /** The session's answer in whatever state, for the dashboard to render. */
    answer(sessionId: string): BridgeAnswer | undefined;
    /**
     * The extension's word on what happened to a delivery. On success the question is resolved:
     * it is dropped, and re-reports of the same block are ignored (see {@link record}).
     */
    resolveAnswer(sessionId: string, id: string, ok: boolean, note?: string): void;
    /** The question that session is parked on, if the bridge has reported one. */
    get(sessionId: string): BridgeQuestion | undefined;
    /** Every parked question, newest first. */
    list(): BridgeQuestion[];
    /** Drop a session's question, once it is answered or its agent is gone. */
    clear(sessionId: string): void;
}
export declare function bridgeQuestions(): BridgeQuestions;
/** Replace the store. Tests only: a module singleton would otherwise leak between them. */
export declare function resetBridgeQuestions(): void;
//# sourceMappingURL=bridge-store.d.ts.map