/**
 * ProviderCliAdapter — Script-based CLI Adapter
 *
 * All CLI providers use versioned scripts (like IDE providers).
 * Scripts are Node.js functions that receive PTY buffer data and return structured results.
 *
 * Required scripts in scripts/{version}/scripts.js:
 *   - detectStatus(input)  → AgentStatus string ('idle' | 'generating' | 'waiting_approval')
 *   - parseSession(input)  → ReadChatResult { messages, status, activeModal, ... }
 *   - parseApproval(input) → ModalInfo | null
 *
 * provider.json contract:
 *   type, name, category: 'cli', binary, spawn, approvalKeys
 *   compatibility: [{ ideVersion, scriptDir }]  ← versioned scripts
 */
import type { CliAdapter, CliLaunchInfo } from '../cli-adapter-types.js';
import type { InteractivePromptResponse } from '../providers/types/interactive-prompt.js';
import { type PtyRuntimeMetadata, type PtyTransportFactory } from './pty-transport.js';
import { type MeshSendKeyItem, type MeshSendKeyName, type CliProviderModule, type CliScripts, type CliSessionStatus, type CliTraceEntry, type ParsedSession } from './provider-cli-shared.js';
import { CliStateEngine, type CliBufferSnapshot } from './cli-state-engine.js';
import { type TurnParseScope } from './provider-cli-parse.js';
import { type ProviderResolutionMeta } from './provider-cli-config.js';
export { normalizeCliProviderForRuntime, type CliApprovalInput, type CliChatMessage, type CliProviderModule, type CliScreenLine, type CliScreenSnapshot, type CliScriptInput, type CliScripts, type CliSessionStatus, type CliStatusInput, type CliTraceEntry, } from './provider-cli-shared.js';
export declare function appendBoundedText(current: string, chunk: string, maxChars: number): string;
export declare class ProviderCliAdapter implements CliAdapter {
    private extraArgs;
    private extraEnv;
    cliType: string;
    readonly cliName: string;
    workingDir: string;
    private provider;
    private ptyProcess;
    private transportFactory;
    private onStatusChange;
    private inApprovalResumeGraceProbe;
    readonly engine: CliStateEngine;
    private responseBuffer;
    private recentOutputBuffer;
    private get parseErrorMessage();
    private providerSessionId;
    private responseTimeout;
    private ready;
    private firstTurnSent;
    private startupBuffer;
    private startupParseGate;
    private startupSettleTimer;
    private spawnAt;
    private startupFirstOutputAt;
    private onPtyDataCallback;
    private pendingOutputParseChunks;
    private pendingOutputParseTimer;
    private ptyOutputChunks;
    private ptyOutputFlushTimer;
    private pendingTerminalQueryTail;
    private lastOutputAt;
    private lastNonEmptyOutputAt;
    private lastScreenChangeAt;
    private lastScreenSnapshot;
    private lastScreenText;
    private lastScreenSnapshotReadAt;
    private staticIdlePollStreak;
    private serverConn;
    private logBuffer;
    private pendingOutboundQueue;
    private pendingOutboundFlushTimer;
    private pendingOutboundFlushInFlight;
    private submitRetryTimer;
    private ptyWriteChain;
    private resizeSuppressUntil;
    private readonly runner;
    /** @deprecated use runner.cliScripts for direct script access */
    get cliScripts(): CliScripts;
    /**
     * Recent script invocations (oldest → newest). Exposed via the
     * `get_chat_debug_bundle` daemon command so anyone debugging a
     * stuck-status regression can read what each detectStatus /
     * parseSession call actually saw and returned, instead of having
     * to instrument the daemon.
     */
    getScriptInvocationTrace(): import("./cli-script-runner.js").CliScriptInvocationTrace[];
    /**
     * Returns the full raw PTY byte stream captured since adapter start.
     * Used by the `record_provider_pty` IPC command to produce fixtures.
     * Bounded by MAX_ACCUMULATED_BUFFER; older bytes may have been dropped.
     */
    getAccumulatedRawBuffer(): {
        text: string;
        droppedChars: number;
    };
    set cliScripts(scripts: CliScripts);
    private runtimeSettings;
    /** Full accumulated rendered PTY transcript for parser/readback use */
    private accumulatedBuffer;
    /** Stateful rendered transcript accumulator; raw debug remains in accumulatedRawBuffer. */
    private transcriptAccumulator;
    /** Full accumulated raw PTY output (with ANSI) */
    private accumulatedRawBuffer;
    /** Current visible terminal screen snapshot */
    private terminalScreen;
    private static readonly MAX_RESPONSE_BUFFER;
    private static readonly MAX_RECENT_OUTPUT_BUFFER;
    private responseBufferDroppedChars;
    private recentOutputDroppedChars;
    private accumulatedBufferDroppedChars;
    private accumulatedRawBufferDroppedChars;
    /** Max accumulated buffer size. Sized to comfortably hold a single long
     *  Hermes turn (tool calls + reasoning + final bubble) without the
     *  rolling window pushing the turn's ╭─ opening line out of view. */
    private static readonly MAX_ACCUMULATED_BUFFER;
    private parsedStatusCache;
    private static readonly SCREEN_SNAPSHOT_MIN_INTERVAL_MS;
    private static readonly TERMINAL_SNAPSHOT_DEFAULT_MAX_BYTES;
    private static readonly TERMINAL_SNAPSHOT_ABSOLUTE_MAX_BYTES;
    private static readonly STATIC_IDLE_POLL_CONFIRM_COUNT;
    private readonly providerResolutionMeta;
    private getBufferState;
    private recordBoundedAppendDrop;
    private readTerminalScreenText;
    private getParseScreenText;
    private shouldReadTerminalScreenSnapshot;
    private resetTerminalScreen;
    private getAccumulatedRawBufferCacheKey;
    private getFreshParsedStatusCache;
    private providerOwnsTranscript;
    private shouldUseFullProviderTranscriptContext;
    /**
     * MULTI-TURN TRANSCRIPT FLICKER (kimi): whether this provider reconstructs
     * its ENTIRE transcript from the rendered PTY buffer on every read and so
     * must be fed the full accumulated buffer rather than the current-turn slice.
     *
     * The turn-scope slice (buildCliParseInput → sliceFromOffset(accumulatedBuffer,
     * scope.bufferStart)) exists so a mid-turn parse only sees the fresh output of
     * the CURRENT turn — correct for status/streaming detection and for providers
     * that reassemble prior history elsewhere (native-history on disk, or a
     * provider-owned full-context parser). But a pure-PTY provider with NO native
     * history and NO provider transcript authority, whose tui parser walks the
     * whole buffer for its per-turn bubble markers (kimi's `●`/`✨` bullets),
     * loses every prior turn's bubbles the instant a new turn starts: bufferStart
     * jumps to the new turn's offset, the slice drops turns 1..n-1, and until the
     * new turn emits its first bullet parseSession returns zero messages — so
     * read_chat momentarily goes EMPTY and the dashboard clears every bubble,
     * then restores them once output arrives (the observed flicker).
     *
     * accumulatedBuffer is the terminal-EMULATED rendered scrollback (overwritten
     * cells collapsed), not raw PTY append, so parsing it in full yields the clean
     * cumulative transcript with no repaint duplication. Scope to exactly this
     * class (tui transcriptPty scope 'buffer' + no native history + not
     * provider-owned) so no other provider's turn-scoped parse changes.
     */
    private parsesFullPtyTranscriptFromBuffer;
    /**
     * The turn scope to feed the transcript parser. Normally the live turn scope
     * (so a mid-turn parse is current-turn-only), but null (= full accumulated
     * buffer) for pure-PTY full-transcript providers so prior turns never drop.
     * See {@link parsesFullPtyTranscriptFromBuffer}.
     */
    private transcriptParseScope;
    private getIdleFinishConfirmMs;
    private getStatusActivityHoldMs;
    private isAutonomousMeshSession;
    private readonly timeouts;
    private readonly approvalKeys;
    private readonly sendDelayMs;
    private readonly sendKey;
    private readonly submitStrategy;
    private readonly requirePromptEchoBeforeSubmit;
    constructor(provider: CliProviderModule, workingDir: string, extraArgs?: string[], extraEnv?: Record<string, string>, transportFactory?: PtyTransportFactory);
    /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
    setCliScripts(scripts: CliScripts): void;
    /** Refresh provider scripts/config used by this adapter without restarting the PTY runtime. */
    refreshProviderDefinition(provider: CliProviderModule): void;
    updateRuntimeSettings(settings: Record<string, any>): void;
    setServerConn(serverConn: any): void;
    setOnStatusChange(callback: () => void): void;
    setInApprovalResumeGraceProbe(probe: () => boolean): void;
    setOnPtyData(callback: (data: string) => void): void;
    private flushPendingOutputParse;
    spawn(): Promise<void>;
    private handleOutput;
    private resolveStartupState;
    private scheduleStartupSettleCheck;
    private waitForInteractivePrompt;
    private clearAllTimers;
    runParseSession(): ParsedSession | null;
    runDetectStatus(text: string): string | null;
    /**
     * WRITE-READINESS ONNOMATCH (opencode): resolve whether the session is
     * genuinely idle *for the purpose of opening the PTY write gate*, honoring
     * the manifest's `dispatchOrder.onNoMatch` policy.
     *
     * The split-brain this closes: the engine's settled evaluation runs
     * detectStatus through parseSession, whose builder collapses a null verdict
     * to `'idle'` (buildParseSessionFromTui: `detectStatus(input) ?? 'idle'`),
     * so the dashboard status flips to idle via `script_detect`. But the
     * write-readiness gates (resolveStartupState / sendMessage recovery) call
     * `runDetectStatus` *directly* and require the literal `=== 'idle'` return —
     * they never apply the `onNoMatch` policy. opencode's only idle cue is the
     * `Ask anything` composer placeholder in the last-8-lines scope with
     * `onNoMatch: preserve-last`; when that placeholder is momentarily out of
     * frame the raw detector returns null, the gate stays shut, `this.ready`
     * never flips, and the first queued prompt sits in `not_ready_pending_prompt`
     * forever (no turn ever starts, so no turn-completion drain fires).
     *
     * The fix keeps the raw detector as the primary signal (unchanged behavior
     * for providers whose detector returns a literal idle) and only falls back
     * when BOTH (a) the manifest policy is idle-preserving (`preserve-last` or
     * `idle`) AND (b) the engine has *durably* settled to idle (no in-flight
     * turn, no modal, no parse error). That guard makes the fallback safe: it
     * cannot open the gate mid-turn or while a modal is up.
     */
    private detectIdleHonoringOnNoMatch;
    runParseApproval(tail: string): {
        message: string;
        buttons: string[];
    } | null;
    private applyParsedSessionMetadata;
    private projectEffectiveStatus;
    getStatus(options?: {
        allowParse?: boolean;
    }): CliSessionStatus;
    /**
     * Script-based full parse — returns ReadChatResult.
     * Called by command handler / dashboard for rich content rendering.
     */
    getScriptParsedStatus(): any;
    invokeScript(scriptName: string, args?: Record<string, any>): Promise<any>;
    private parseCurrentTranscript;
    /** Whether this adapter has CLI scripts loaded */
    hasCliScripts(): boolean;
    /**
     * Resolves an action (like 'fix' lint error) from the dashboard.
     * Uses resolveAction script if available, otherwise falls back to standard text.
     */
    resolveAction(data: any): Promise<void>;
    setInteractivePromptResponse(_response: InteractivePromptResponse): Promise<void>;
    private isSubmitStuck;
    private hasMeaningfulResponseBufferLocal;
    /** Serialize `data` onto the per-session PTY write chain. Returns a promise
     *  that resolves (or rejects) with THIS write's own outcome, so callers keep
     *  their existing "await my write completion" semantics. The underlying chain
     *  link swallows its own error before releasing the next write, so a failed
     *  write surfaces to its caller but never wedges subsequent writes behind a
     *  permanently-rejected tail (FIFO order preserved). */
    private writeToPty;
    private doWriteToPty;
    /** Write `data` to the PTY in bounded, surrogate-safe chunks with a short
     *  inter-chunk gap (win32 paced write). Awaits each chunk's write and the gap
     *  so the returned promise resolves only after the FINAL chunk (carrying any
     *  trailing submit key) has been written. */
    private writeWin32Chunked;
    private resetPendingSendState;
    private commitSendUserTurn;
    private armResponseTimeout;
    private writeSubmitKeyForRetry;
    private writeStuckRetry;
    private retrySubmitIfStuck;
    private retryImmediateSubmitIfStuck;
    private submitSendKey;
    private submitImmediatePrompt;
    private waitForEchoAndSubmit;
    sendMessage(text: string, options?: {
        force?: boolean;
        meshTaskId?: string;
    }): Promise<void>;
    forceSendMessage(text: string, meshTaskId?: string): Promise<void>;
    private waitForForceSubmitSettle;
    private enqueuePendingOutboundMessage;
    private shouldQueuePendingOutboundMessage;
    private schedulePendingOutboundFlush;
    private flushPendingOutboundQueue;
    private sendMessageNow;
    getPartialResponse(): string;
    getDebugSnapshot(): Record<string, unknown>;
    getRuntimeMetadata(): PtyRuntimeMetadata | null;
    /**
     * Launch metadata for the dashboard Session info panel. Re-derives the spawn plan
     * (pure — same inputs the live PTY was spawned with) so the dashboard sees the
     * resolved binary, full arg vector, and cwd without us having to persist it.
     * extraEnv values are intentionally dropped (keys only) so secrets passed at
     * launch time never reach the dashboard.
     */
    getLaunchInfo(): CliLaunchInfo;
    updateRuntimeMeta(meta: Record<string, unknown>, replace?: boolean): void;
    cancel(): void;
    saveAndStop(): Promise<void>;
    private waitForStopped;
    shutdown(): void;
    detach(): void;
    clearHistory(): void;
    isProcessing(): boolean;
    isReady(): boolean;
    get currentStatus(): CliSessionStatus['status'];
    set currentStatus(v: CliSessionStatus['status']);
    get isWaitingForResponse(): boolean;
    set isWaitingForResponse(v: boolean);
    get activeModal(): {
        message: string;
        buttons: string[];
    } | null;
    set activeModal(v: {
        message: string;
        buttons: string[];
    } | null);
    get currentTurnScope(): TurnParseScope | null;
    set currentTurnScope(v: TurnParseScope | null);
    get currentTurnTaskId(): string | null;
    get currentTurnStartedAt(): number;
    get responseEpoch(): number;
    set responseEpoch(v: number);
    get submitRetryUsed(): boolean;
    set submitRetryUsed(v: boolean);
    get submitRetryPromptSnippet(): string;
    set submitRetryPromptSnippet(v: string);
    get responseSettleIgnoreUntil(): number;
    set responseSettleIgnoreUntil(v: number);
    get submitPendingUntil(): number;
    set submitPendingUntil(v: number);
    get lastApprovalResolvedAt(): number;
    set lastApprovalResolvedAt(v: number);
    get providerErrorMessage(): string | null;
    get providerErrorReason(): string | null;
    get pendingScriptStatus(): 'generating' | 'waiting_approval' | null;
    get pendingScriptStatusSince(): number;
    get finishRetryCount(): number;
    set finishRetryCount(v: number);
    get traceSessionId(): string;
    get traceEntries(): CliTraceEntry[];
    get statusHistory(): {
        status: string;
        at: number;
        trigger?: string;
    }[];
    get traceSeq(): number;
    /** Expose engine's evaluateSettled for test access */
    evaluateSettled(): void;
    /** Expose engine's scheduleSettle for test access */
    scheduleSettle(): void;
    /** Expose engine's clearIdleFinishCandidate for test access */
    clearIdleFinishCandidate(reason: string): void;
    /** Expose engine's finishResponse for test access */
    finishResponse(): void;
    /** Returns a point-in-time snapshot of all buffer/screen state for external consumers (e.g. CliStateEngine). */
    getSnapshot(): CliBufferSnapshot;
    isAlive(): boolean;
    /**
     * MESH-READ-TERMINAL (feature 2: RAW terminal read). Narrow, least-privilege
     * read of the CURRENT rendered viewport for mesh_read_terminal. Deliberately
     * NARROWER than getSnapshot()/getDebugSnapshot():
     *  - It returns ONLY the terminal's current rendered viewport (what a human
     *    would see on screen right now), the cursor position and the viewport
     *    size. NO debug buffers, NO parser/FSM state, NO scrollback/history.
     *  - It does NOT call getParseScreenText() (which may graft an older snapshot
     *    onto the current frame for parse accuracy) — the caller asked for the
     *    live viewport, not a parse-optimized composite.
     *  - The payload is bounded in BYTES (UTF-8) with bottom-tail preservation so
     *    a screen of multi-byte glyphs can never exceed the MCP payload cap. See
     *    truncateToByteTailByLine.
     *
     * SECURITY NOTE: the raw viewport can contain tokens / command args / env
     * values / user data. Callers MUST gate this on mesh ownership and MUST NOT
     * log the returned text. Opt-in redaction is intentionally out of scope for
     * this feature and left as a future enhancement.
     *
     * `maxBytes` is clamped to [1KiB, ABSOLUTE_MAX] (default 32KiB, hard cap 64KiB).
     */
    getTerminalScreenSnapshot(maxBytes?: number): {
        text: string;
        cursor: {
            col: number;
            row: number;
        };
        cols: number;
        rows: number;
        truncated: boolean;
        originalBytes: number;
        returnedBytes: number;
        hash: string;
    };
    /**
     * MESH-SEND-KEYS (feature 3: key injection). Inject a STRUCTURED key sequence
     * into the PTY for the mesh_send_keys tool. Reuses the same serialized write
     * path as sends (writeToPty via writeRaw semantics) so the injection FIFO-
     * orders behind any in-flight write.
     *
     * Two guards run INSIDE this method, immediately before the write, so they see
     * a consistent snapshot of the adapter's submit/echo/queue state (no async gap
     * between check and write — the encode is synchronous and the write is chained
     * atomically after it):
     *
     *  1. submit-race recheck (echo-gate): even though writeToPty is FIFO, an
     *     already-SCHEDULED echo-gated Enter (engine.submitPendingUntil in the
     *     future), an armed stuck-submit retry (submitRetryTimer), or an in-flight
     *     pending-outbound flush can submit at a DIFFERENT tick than our write —
     *     so an injected literal could be submitted by a pending Enter, or our
     *     ENTER could submit a half-typed pending body. When any of those is live
     *     we REFUSE the injection rather than interleave.
     *
     *  2. modal fail-closed: if the session is parked on an actionable approval
     *     modal, refuse a NON-destructive injection (ENTER/text/arrows) and direct
     *     the caller to mesh_approve — so a modal choice can't be confirmed via
     *     send_keys to bypass the approval policy. (A destructive ESC/CTRL_C, which
     *     dismisses rather than confirms, is allowed to proceed past this gate; it
     *     is separately gated by confirm_destructive + policy at the tool layer.)
     *
     * The caller (tool layer) owns the destructive-key double gate and the audit
     * ledger. This method NEVER logs the literal text (only key enums / byte len).
     */
    injectKeys(items: MeshSendKeyItem[], opts?: {
        allowModalOverride?: boolean;
    }): Promise<{
        ok: true;
        keys: MeshSendKeyName[];
        hasDestructive: boolean;
        submits: boolean;
        bytes: number;
    } | {
        ok: false;
        refused: 'submit_race' | 'actionable_modal';
        keys: MeshSendKeyName[];
        hasDestructive: boolean;
    }>;
    flushOutboundQueue(): void;
    writeRaw(data: string | Buffer): Promise<void>;
    resolveModal(buttonIndex: number): void;
    getApprovalKeyForIndex(buttonIndex: number): string | undefined;
    /** Returns true if an approval was resolved within the adapter's cooldown window. */
    isApprovalRecentlyResolved(): boolean;
    resize(cols: number, rows: number): void;
    private getParsedDebugState;
    getDebugState(): Record<string, any>;
    getTraceState(limit?: number): Record<string, any>;
    getProviderResolutionMeta(): ProviderResolutionMeta;
}
