/**
 * Bounded rolling-TAIL accumulator for streaming exec output (sema D2) — FIRST-PARTY, vendor-independent.
 *
 * A model-driven `yes` / `cat big.bin` over a 120s/600s shell can emit gigabytes; accumulating it unbounded OOMs
 * a durable worker. We keep only the LAST `maxBytes` — the tail (errors + the exit code are at the end, which is
 * what the model needs) — dropping the head as it overflows, plus a count of dropped bytes for a visible marker.
 *
 * This lives in `src/core/` — NOT `src/vendor/` — on purpose: it is OUR robustness invariant, shared by the
 * (currently-vendored) NodeExecutionEnv AND a downstream exec adapter (service host/ssh/adb/docker/k8s).
 * One implementation + ONE model-facing marker text = zero drift, and the public export points at a STABLE path
 * that survives the planned de-vendoring (when NodeExecutionEnv is naturalized out of `src/vendor/`, nothing rebinds).
 */
import { StringDecoder } from "node:string_decoder";
export declare const MAX_EXEC_OUTPUT_BYTES: number;
/**
 * Push-based bounded tail: feed it raw output chunks (Buffers); it retains only the last `maxBytes`, dropping the
 * head incrementally so memory is O(maxBytes) regardless of total output. Byte-precise (the OOM bound is bytes).
 */
export declare class RollingTailBuffer {
    private readonly maxBytes;
    private readonly chunks;
    private size;
    private headDropped;
    private sourceSkipped;
    constructor(maxBytes?: number);
    /** Append a chunk; evict head bytes (whole or partial chunks) until within `maxBytes`. */
    push(chunk: Buffer): void;
    /** Account bytes that were dropped BEFORE ever reaching the buffer (e.g. a spool reader skipping
     *  un-retainable backlog at the source). They join `droppedBytes` so `markTruncated` discloses the
     *  TRUE number of missing bytes — recording them here instead of pushing keeps the drop O(1). */
    recordSkippedBytes(n: number): void;
    /** The retained tail decoded as UTF-8, plus how many bytes are missing in total (head evictions +
     *  source skips — the foreground `markTruncated` disclosure keeps the combined figure). */
    result(): {
        text: string;
        droppedBytes: number;
    };
    /** The retained window's RAW bytes — cursor slicing must happen in the byte domain
     *  (re-encoding a lossily-decoded string inflates every invalid byte to a 3-byte U+FFFD and lands
     *  byte cursors on the wrong offsets). */
    bytes(): Buffer;
    /** Head-eviction count ALONE — the retention window is [headDroppedBytes, pushed-total)
     *  in the accepted-byte domain; source skips must not shift this origin. */
    headDroppedBytes(): number;
    /** Source-skip count alone (see {@link recordSkippedBytes}) — poll disclosure reports
     *  the per-poll increment of this figure instead of silently absorbing it into the window origin. */
    sourceSkippedBytes(): number;
}
/** Per-stream cursor state for incremental (poll-style) consumption of a
 *  {@link RollingTailBuffer}. All counters live in the ACCEPTED-byte domain (bytes actually pushed);
 *  source skips are tracked separately for disclosure. Module-level export (not on the package
 *  surface) so the slice arithmetic is unit-pinnable without an 8MB integration harness. */
export interface StreamCursorState {
    tail: RollingTailBuffer;
    /** Consumer position in the accepted-byte domain (bytes already delivered). */
    cursorBytes: number;
    /** Bytes actually pushed into `tail` (excludes source skips — those never had content to deliver). */
    acceptedBytes: number;
    /** `tail.sourceSkippedBytes()` as of the last slice — the delta is disclosed exactly once. */
    disclosedSkippedBytes: number;
    /** Incremental UTF-8 decoder (CC 2.1.220 shape: StringDecoder holds a split multi-byte char across
     *  reads instead of emitting replacement chars at every read boundary). */
    decoder: StringDecoder;
}
/** Fresh cursor state over a new tail. */
export declare function newStreamCursorState(tail?: RollingTailBuffer): StreamCursorState;
/**
 * Slice the undelivered increment out of a rolling tail — in the BYTE domain, decoded
 * incrementally. Replaces the former decode→re-encode→byte-offset-subarray shape, which mixed two
 * domains: byte cursors landed inside a re-encoded string whose length differs from the raw stream as
 * soon as any invalid-UTF-8 byte sits in the window (each inflates to a 3-byte U+FFFD), making every
 * later poll re-deliver the same trailing garbage forever (a sleeping process "producing" output).
 *
 *  - Continuity: a multi-byte char split across two slices is held by the decoder and completed on the
 *    next slice — zero replacement chars, zero duplication.
 *  - Discontinuity (`droppedBeforeCursor > 0`): the decoder is rebuilt, so bytes on either side of a
 *    hole are never joined into a fake character.
 *  - `terminal`: flush the decoder (a trailing split char becomes one honest U+FFFD) — without this
 *    the last 1–3 bytes of a finished stream would never surface.
 *
 * `droppedBeforeCursor` = head-evicted bytes the cursor never got to + the NEW source-skip increment
 * since the previous slice (formerly silent: skips shifted the window origin instead, re-delivering
 * already-served bytes with `truncated` unset).
 */
export declare function sliceStreamIncrement(s: StreamCursorState, terminal: boolean): {
    inc: string;
    droppedBeforeCursor: number;
};
/**
 * Prepend a model-visible marker when `droppedBytes > 0` (only the TAIL was kept). The SINGLE source of the
 * marker wording so the engine and any downstream adapter show the model the SAME thing.
 */
export declare function markTruncated(text: string, droppedBytes: number): string;
