import type { CodexBridgeEvent } from './codex-transcript.js'; /** A verified submit may be parked in a type-ahead queue before its transcript * user event is written. Keep that hand-off busy for a bounded interval: long * enough for the active turn to finish and dequeue it, but never forever if * the CLI accepted the keypress without producing a structured event. */ export declare const STRUCTURED_SUBMIT_START_GRACE_MS = 20000; /** Maximum time an unconfirmed worker mark may remain at the attribution * head after the adapter write/verification path stops. This lease never * contributes to lifecycle busy: it exists only so a late transcript user * event can still claim the mark without allowing a silent write to wedge * every later turn forever. */ export declare const STRUCTURED_UNCONFIRMED_ATTRIBUTION_GRACE_MS = 20000; /** Maximum time a worker may wait for an adapter/history verification call. * This covers Codex/CoCo's in-band polling plus the 20s deferred recheck, * while remaining bounded if an adapter promise or recheck is stranded. */ export declare const STRUCTURED_SUBMIT_VERIFICATION_GRACE_MS = 30000; export interface CodexPendingTurn { turnId: string; dispatchAttempt?: number; started: boolean; contentFingerprint?: string; /** Wall-clock millis when mark() was called. The emit gate uses this as * the lower bound of the "did `botmux send` happen for this turn?" * window. Optional only for legacy / test-injected turns. */ markTimeMs?: number; /** Wall-clock millis when an authoritative adapter/history check confirmed * the submit. Unverified writes deliberately leave this unset. */ submitConfirmedAtMs?: number; /** Wall-clock millis anchoring the bounded attribution-only lease for an * unconfirmed mark. Unlike verification/confirmation leases, this never * gates screen-ready or reports the CLI busy. */ unconfirmedAttributionStartedAtMs?: number; /** Wall-clock millis when worker-side authoritative submit verification * began. This closes the race where screen-ready arrives while writeInput * is still polling history, before it can return `submitted: true`. */ submitVerificationStartedAtMs?: number; /** Set once an assistant_final event closes this turn. */ finalText?: string; /** Explicit transcript terminal semantics. Undefined keeps the historical * assistant-final => completed behaviour. */ terminalStatus?: 'completed' | 'failed' | 'ambiguous'; terminalErrorCode?: string; terminalErrorSummary?: string; /** Set when this turn was synthesised from a user_message that didn't * match any pending Lark fingerprint. Adopt-only. The worker emit path * formats these with both userText and finalText under a "终端本地对话" * header — same rationale as Claude's BridgeTurnQueue local turns. */ isLocal?: boolean; /** For local turns: the user's typed text, surfaced alongside the * assistant reply so the Lark thread sees both sides of the exchange. */ userText?: string; sourceSessionId?: string; /** True when the turn was delivered via Codex RPC (turn/start) and the * app-server has acknowledged it. RPC turns have no local transcript to * ingest, so they can never reach the started state; this flag keeps the * lifecycle gate asserted for the full server-side execution instead of * letting the bounded 20s confirmation lease expire mid-turn (which would * falsely release idle and prune a still-running turn). Cleared by the * terminal edge or an explicit stop. */ rpcActive?: boolean; } export declare class CodexBridgeQueue { private readonly now; private seen; private queue; private collecting; private localTurnsEnabled; private bufferedUnmatched; private lastClosedAssistantFinalTimeMs; /** Lower bound (ms) for synthesising local turns — protects against a * fresh-empty attach replaying historical iTerm conversation as * "live" local input. Typically set to the moment adopt was wired up. */ private localLowerBoundMs; constructor(now?: () => number); /** Register events as historical without producing pending-turn side * effects. Used at attach time when resume mode wants to swallow prior * conversation as already-processed. */ absorb(events: CodexBridgeEvent[]): void; /** Toggle adopt-mode local-turn synthesis. `lowerBoundMs` (typically * Date.now() at adopt-time) protects against a fresh-empty attach * feeding historical user_messages back as "live" local turns. */ setLocalTurns(enabled: boolean, lowerBoundMs?: number): void; /** Push a pending Lark turn anchored to the message text. The fingerprint * derived from `message` is what the upcoming `user` event must contain * to start this turn. Pre-path-known marking is allowed: the worker can * call this before late-attach has located the rollout file, and the * ingest call after attach will still match correctly. */ mark(turnId: string, message: string, markTimeMs?: number, dispatchAttempt?: number): void; /** Drop all pending turns. Used when the worker decides it can't reliably * attribute future events (e.g. a teardown). */ clearPending(): CodexPendingTurn[]; /** Remove one exact worker delivery attempt. Submit-confirmation cleanup is * pre-start-only by default: once the transcript has started a turn, only a * structured terminal may retire it. An authoritative failed/ambiguous * terminal may opt into removing a started attempt via `allowStarted`. * Matching dispatchAttempt keeps a replay of the same turnId isolated from * the retired delivery attempt. */ dropPendingTurn(turnId: string, dispatchAttempt?: number, allowStarted?: boolean): CodexPendingTurn | null; /** Remove expired pre-start queue heads that never reached transcript * start, whether positively confirmed or only retained for attribution. * Only the first unresolved fingerprint(s) are eligible, and never * while an earlier started turn is still running: that predecessor's final * is the dequeue boundary that refreshes the next legitimate type-ahead * lease. Dropping a stale head replays buffered events immediately, so a * later real turn can become started instead of remaining hidden behind a * dead fingerprint. */ pruneExpiredPreStartHeads(nowMs?: number): CodexPendingTurn[]; /** Record positive submit evidence from an adapter/history check. The turn * can still be waiting in the CLI's type-ahead queue, so this starts a * bounded hand-off lease until its transcript user event appears. */ confirmPendingTurn(turnId: string, confirmedAtMs?: number, dispatchAttempt?: number): boolean; /** Mark a turn as actively running server-side via Codex RPC. The app-server * ack for turn/start is authoritative confirmation that execution has begun, * but no local transcript event will follow to flip started. This flag keeps * the lifecycle gate asserted and protects the turn from lease expiry pruning * until the terminal edge (or an explicit stop) clears it. */ markRpcActive(turnId: string, dispatchAttempt?: number): boolean; /** Clear the server-side active flag when an RPC turn reaches a terminal edge * or is otherwise retired. Without this, a completed RPC turn would keep the * lifecycle gate asserted forever (permanent false-busy). */ stopRpcActive(turnId: string, dispatchAttempt?: number): boolean; /** Start bounded adapter/history verification before awaiting writeInput. */ beginSubmitVerification(turnId: string, startedAtMs?: number, dispatchAttempt?: number): boolean; /** Finish verification without positive submit evidence. A bare mark remains * available for transcript attribution but no longer gates screen-ready. */ finishSubmitVerification(turnId: string, finishedAtMs?: number, dispatchAttempt?: number): boolean; /** Exact-attempt existence check for deferred callbacks. A replay reuses * turnId with a higher dispatchAttempt, so an old timer must treat that as * a missing target rather than mutating the new delivery generation. */ hasPendingTurn(turnId: string, dispatchAttempt?: number): boolean; /** True when buffered transcript replay has already closed this exact turn * before its RPC turn/start continuation installs rpcActive. */ hasTerminalTurn(turnId: string, dispatchAttempt?: number): boolean; /** True while the transcript proves a turn is running, or while a verified * submit is in the bounded pre-start hand-off window. A bare worker mark is * never authoritative, preventing a dropped Enter from causing permanent * false-busy. */ hasBlockingTurn(nowMs?: number): boolean; /** Remaining bounded pre-start verification/confirmation lease. The worker * uses this to re-drive a previously rejected ready signal once every active * lease expires. Started turns return undefined because their eventual * assistant_final is the authoritative re-drive. */ preStartLeaseRemainingMs(nowMs?: number): number | undefined; /** Process newly-appended events. Idempotent on uuid: events with seen * uuids are skipped, so callers can replay safely. */ ingest(events: CodexBridgeEvent[]): void; private replayBufferedUnmatched; private rememberUnmatched; /** Refresh the next queued submit from the locally-observed terminal edge. * External transcript clocks may be skewed, so lease boundedness must use * this process's clock for both successful and aborted predecessors. */ private refreshNextPreStartLease; private ingestOne; /** Pop FIFO any leading turn that is started AND observed a terminal edge. * Empty final text closes a durable turn without producing final_output. */ drainEmittable(): CodexPendingTurn[]; size(): number; /** Test helper — peek the queue without mutating. */ peek(): readonly CodexPendingTurn[]; } /** Explicit mutation boundary for lease expiry. Pruning can replay a buffered * successor user+final pair, so callers must drain/emit in the same call * stack; keeping that invariant here prevents a status/query path from * silently creating an unconsumed completion. */ export declare function pruneExpiredPreStartHeadsAndEmit(queue: CodexBridgeQueue, emitReady: () => void, nowMs?: number, /** Settle exact durable attempts before a replayed successor is emitted. * The queue removal can expose buffered successor user/final events, so * running this after emitReady would publish N+1 ahead of N's terminal. */ onDropped?: (dropped: readonly CodexPendingTurn[]) => void): CodexPendingTurn[]; //# sourceMappingURL=codex-bridge-queue.d.ts.map