/**
 * Adopt-bridge turn attribution state machine.
 *
 * Pure (no fs / IPC / timers) so the worker can wrap it with watchers and
 * tests can drive it deterministically. The worker feeds it transcript
 * events (already drained from JSONL) and Lark-message markers; this class
 * decides which assistant uuids belong to which Lark turn.
 *
 * Attribution rule:
 *   - mark()           — pushes a new pending turn entry (state: not started)
 *   - ingest(events)   — for each new user/assistant event:
 *       * user event → the earliest unstarted pending turn whose fingerprint
 *         matches becomes 'started' (its assistantUuids will collect from
 *         now on). A user event that does NOT match any pending fingerprint
 *         (or arrives with no pending Lark turn at all) is treated as
 *         **local terminal input**: a synthetic local turn is created on
 *         the spot, started immediately, and inserted ahead of any
 *         still-unstarted Lark turns so emit ordering reflects when the
 *         user event actually landed in the transcript. The local turn is
 *         emitted with `isLocal: true` so the worker can format it with a
 *         "user typed in the terminal" marker for the Lark thread.
 *       * assistant text event (non-sidechain) → appended to the
 *         currently-collecting turn (Lark or local), if any.
 *   - drainEmittable() — pops any leading turn that has been started AND has
 *     accumulated at least one visible assistant-text uuid. Started turns with no text
 *     yet (Claude is mid-tool-use) stay queued for the next idle.
 *
 * Baseline (`absorb()`) takes a batch of historical events and registers
 * their uuids as already-seen so future ingest doesn't double-attribute.
 */
import { normaliseForFingerprint, type TranscriptEvent } from './claude-transcript.js';
export { normaliseForFingerprint };
export interface BridgePendingTurn {
    turnId: string;
    started: boolean;
    assistantUuids: string[];
    /** Set when this turn was synthesised from a local-terminal user event
     *  (no matching Lark fingerprint). Causes the worker emit path to format
     *  the Lark message with both user text and assistant text under a
     *  "🖥️ 终端本地对话" header — otherwise the user would see an orphan
     *  reply with no prompt for context. Lark-driven turns keep this unset. */
    isLocal?: boolean;
    /** Transcript uuid of the user event that started this turn. Stored for
     *  local turns so emit can fetch the user-typed content from the source
     *  jsonl alongside the assistant uuids. Lark turns don't need it because
     *  the user content is already known on the daemon side. */
    userUuid?: string;
    /** A short substring of the Lark message that we expect to find inside
     *  the next matching `user` event's content. When set, only a user event
     *  whose stringified content contains this fingerprint is allowed to
     *  start the turn. Local-terminal input (whose content won't contain
     *  the Lark fingerprint) leaves the turn unstarted. */
    contentFingerprint?: string;
    /** Full normalised content of the Lark message. Used by the rotation
     *  fallback's recovery path to gate a switch into an UNKNOWN sessionId
     *  on exact equality with a user/queue event in that file — much
     *  stronger than the substring fingerprint check, which can't tell
     *  "test" from "run tests" across sibling panes. Stored in addition to
     *  `contentFingerprint` (not instead of) because in-pane known-sid
     *  candidates still benefit from the cheaper substring path. */
    contentNormalized?: string;
    /** JSONL file the turn's user event was first seen in. Stamped by ingest()
     *  when the turn transitions to started. Lets the emit step re-read text
     *  from the original transcript even after a sessionId rotation has
     *  pointed bridgeJsonlPath at a *different* file — without this stamp,
     *  uuid → text resolution would fail and the reply would be silently
     *  dropped. */
    sourceJsonlPath?: string;
    /** Wall-clock millis when mark() was called. Lets the fingerprint-based
     *  rotation fallback bound its scan to events written after we marked
     *  the turn — short fingerprints ("hello", "test") would otherwise risk
     *  matching pre-existing user lines in unrelated sibling jsonls. */
    markTimeMs?: number;
}
/** Trim a Lark message into a stable fingerprint. Keeps a leading window
 *  of non-whitespace-collapsed content; long enough to disambiguate, short
 *  enough that minor formatting differences (newlines, attachment hints
 *  appended below) don't break the match. */
export declare function makeFingerprint(message: string, len?: number): string | undefined;
export declare class BridgeTurnQueue {
    private seen;
    private queue;
    private collecting;
    /** Register events as historical — their uuids are now considered seen
     *  but no attribution happens. Used at attach time to baseline. */
    absorb(events: TranscriptEvent[]): void;
    /** Push a new pending turn for the next Lark message. `contentFingerprint`
     *  (when set) restricts which user event can start this turn — only a
     *  user event whose content contains the fingerprint qualifies. Pass
     *  `undefined` to start on the next user event regardless (legacy).
     *
     *  `markTimeMs` is captured here so the rotation fallback can bound its
     *  fingerprint scan to events written after this point — protects short
     *  fingerprints from matching old history in unrelated sibling jsonls. */
    mark(turnId: string, contentFingerprint?: string, markTimeMs?: number, contentNormalized?: string): string;
    /** Drop all pending turns. Used when the worker discovers it can't
     *  reliably attribute future events (e.g. baseline raced with a turn
     *  already in flight) and wants to clear the slate. */
    clearPending(): BridgePendingTurn[];
    /** Drop a specific pending turn by turnId iff it has not yet started
     *  collecting assistant text. Returns the dropped turn or null if not
     *  found / already started. Used by the worker when a writeInput's
     *  deferred recheck conclusively fails — the user has been notified
     *  the message was lost, so keeping a fingerprint-bearing mark around
     *  only fuels the per-tick rotation-fallback scan that already
     *  spammed 99% CPU once (no jsonl line will ever match). */
    dropPendingTurn(turnId: string): BridgePendingTurn | null;
    /** Sweep pending (unstarted) turns whose mark is older than `maxAgeMs`.
     *  Returns the dropped turns for logging. Belt-and-braces backstop for
     *  any future code path that leaves an unstarted mark stranded — without
     *  it, `maybeSwitchBridgeJsonl` would keep doing full-directory jsonl
     *  scans every poll tick until the worker restarts. Started turns are
     *  never expired here: once Claude actually wrote the user line, the
     *  turn is collecting assistant text and we want to wait however long
     *  the model takes. */
    pruneExpired(maxAgeMs: number, now?: number): BridgePendingTurn[];
    /** Process newly-appended events. Idempotent on uuid: events with seen
     *  uuids are skipped, so callers can safely replay.
     *
     *  `sourceJsonlPath` (when provided) is stamped onto a turn at the moment
     *  it transitions from "pending" to "started" — so that emit-time text
     *  resolution reads the same transcript file the user/assistant uuids
     *  were originally observed in. Without this, a sessionId rotation
     *  between ingest and emit would silently drop the reply, since the
     *  global current jsonl path would no longer contain those uuids. */
    ingest(events: TranscriptEvent[], sourceJsonlPath?: string): void;
    /** Shared turn-start handler. Called for both `role:user` and
     *  `attachment(queued_command)` events once meaningfulness has been
     *  established by the caller. Encapsulates:
     *    1. HOL-block drop of the previous collecting turn when it got no
     *       assistant text (Claude moved on).
     *    2. Fingerprint-gated start of the earliest unstarted Lark turn,
     *       falling through to local-turn synthesis on mismatch.
     *    3. markTimeMs override to the transcript event's own timestamp —
     *       critical for type-ahead, where the original markTimeMs (set when
     *       the worker wrote to PTY) can be many seconds earlier than the
     *       moment Claude actually dequeues and starts processing the turn.
     *       The bridge-fallback gate's [markTimeMs, nextBoundaryMs) window
     *       MUST anchor on the latter, otherwise a `botmux send` from the
     *       previous turn can leak into the next turn's window and the
     *       suppression decision flips to the wrong turn (real reply
     *       suppressed, fallback shown — exactly what the type-ahead-disable
     *       in commit b2d9791 was protecting against). */
    private handleTurnStart;
    /** Pop FIFO any leading turn that's started AND has assistant text.
     *  Returns the popped turns in order; the caller is responsible for
     *  rebuilding the text payload from the assistant uuids. */
    drainEmittable(): BridgePendingTurn[];
    /** Number of queued (not-yet-emitted) Lark turns. */
    size(): number;
    /** Test helper — peek the queue without mutating. */
    peek(): readonly BridgePendingTurn[];
}
//# sourceMappingURL=bridge-turn-queue.d.ts.map