import type { ConversationRuntimeFailure } from './conversation.js';
import type { PickerItem } from './tui/picker.js';
export type FailedContinuationSubmitAction = 'submit_text' | 'retry_failed_continuation' | 'dedupe_retry_failed_continuation' | 'guide_after_repeated_failed_continuation' | 'guide_after_expensive_failure';
export declare function isRetryEligibleContinuationFailure(failure: ConversationRuntimeFailure | null | undefined): failure is ConversationRuntimeFailure;
export declare function isExpensiveFailedContinuationFailure(failure: ConversationRuntimeFailure | null | undefined): failure is ConversationRuntimeFailure;
export declare function isContinuationRetryInput(text: string): boolean;
export declare function isFailedRequestRecoveryInput(text: string): boolean;
export declare function decideFailedContinuationSubmitAction(options: {
    text: string;
    runtimeFailure: ConversationRuntimeFailure | null | undefined;
    isRetryingFailedContinuation: boolean;
    failedContinuationAttemptCount: number;
}): FailedContinuationSubmitAction;
export declare function formatContinuationRetryStatus(_failure: ConversationRuntimeFailure): string;
export declare function formatRepeatedContinuationRetryGuidance(_failure: ConversationRuntimeFailure, attemptCount: number): string;
export declare function formatExpensiveFailureGuidance(failure: ConversationRuntimeFailure): string;
export declare function shouldScheduleRuntimeAutoRetry(options: {
    runtimeFailure: ConversationRuntimeFailure | null;
    taskAborted: boolean;
    clearEpochUnchanged: boolean;
    currentRetryCount: number;
    retryLimit: number;
    hasQueuedInput: boolean;
    /**
     * 0.13.63 (C): kinds of recent consecutive auto-retry failures, in
     * order. Caller maintains a rolling window (e.g. last 3). When the
     * incoming failure plus the tail of this list adds up to
     * SAME_KIND_RETRY_LIMIT prior same-kind attempts, refuse to
     * schedule further retries. Optional for back-compat — when
     * omitted, behaves like 0.13.62 (only retryLimit caps).
     */
    recentRetryKinds?: string[];
}): boolean;
export declare function shouldDrainQueuedInputAfterTurn(options: {
    hasQueuedInput: boolean;
    taskFailed: boolean;
    autoRetryFailure: ConversationRuntimeFailure | null;
}): boolean;
export declare function shouldQueueSubmitBehindRunningTask(options: {
    isLoading: boolean;
    hasActiveTask: boolean;
    hasScheduledAutoRetry: boolean;
}): boolean;
export type ExitInterruptAction = 'arm' | 'confirm' | 'ignore_duplicate';
export declare const EXIT_CONFIRM_WINDOW_MS = 1500;
export declare const EXIT_CONFIRM_MIN_INTERVAL_MS = 150;
export declare function countInterruptsInInput(input: string, key: {
    ctrl?: boolean;
}): number;
export declare function decideExitInterruptAction(options: {
    nowMs: number;
    lastExitAttemptMs: number;
    interruptsInInput?: number;
    confirmWindowMs?: number;
    minConfirmIntervalMs?: number;
}): ExitInterruptAction;
/**
 * Replaces pseudo tool-call markers in streaming assistant text with a
 * single dim placeholder on first occurrence per turn. Subsequent matches
 * within the same turn are silently dropped (to avoid repeated noise).
 *
 * State must be reset at turn end by resetting `state.elided = false`.
 *
 * Known limitations (accepted trade-offs):
 * 1. Chunk-level regex cannot distinguish legitimate backtick-wrapped
 *    discussion of the syntax from raw model hallucination. Real-world
 *    frequency of model pseudo-tool-call emission far exceeds user
 *    questions about the syntax, so false-positives on legit quotes are
 *    rare and recoverable (the elided placeholder signals what happened).
 * 2. Markers split across streaming-chunk boundaries leak the fragments
 *    on either side. e.g. chunk "`abc[TO`" + chunk "`OL_CALL]{...}[/TOOL_CALL]`"
 *    leaves "`OL_CALL]{...}`" visible on the second chunk because neither
 *    chunk alone contains a full marker. Best-effort guard, not a hard
 *    filter. Most real streams emit markers in a single chunk.
 */
export declare function scrubPseudoToolCall(chunk: string, state: {
    elided: boolean;
}): string;
export type AnchorTier = 'conclusion' | 'next' | 'evidence' | 'uncertainty';
export interface ComposeState {
    seenAnchors: Set<AnchorTier>;
    leftover: string;
}
/**
 * Pre-transform a streaming assistant chunk. Recognizes semantic anchors
 * (Conclusion / Next / Evidence / Uncertainty in Chinese + English) at
 * line-start or sentence-start positions and rewrites them into markdown
 * shapes the renderer already handles. Falls back to sentence-level
 * newline insertion when no anchor is present and prose is long.
 *
 * Pure function. Mutates `state` in place for cross-chunk continuity.
 *
 * Known limitations (accepted trade-offs):
 * 1. Chunk-level regex cannot distinguish legitimate backtick-wrapped
 *    discussion of the syntax (e.g. the string `Conclusion:` inside a
 *    code span) from raw anchor usage. Real-world frequency of model
 *    hallucination vastly exceeds user questions about the syntax.
 * 2. An anchor straddling the soft-cap (512 chars) leftover flush may
 *    leak fragments on either side, because the tail was forced through
 *    processing before the next chunk could join it. Pathological only —
 *    realistic streams emit anchors well within one line before flushing.
 * 3. The CJK detector `/[\u4e00-\u9fff]/` covers CJK Unified Ideographs
 *    only. Japanese Hiragana/Katakana-only prose or Korean Hangul-only
 *    prose would use the 80-char Latin threshold; mixed content with
 *    any CJK Unified char correctly uses the 40-char CJK threshold.
 */
export declare function composeAssistantChunk(chunk: string, state: ComposeState): string;
export interface AssistantStreamRenderer {
    push(chunk: string): string;
    flush(): string;
}
export type SafeRenderFn = (fn: () => string, fallback?: string) => string;
export interface DrainAssistantStreamBoundaryOptions {
    composeState: ComposeState;
    renderer: AssistantStreamRenderer;
    appendRendered: (rendered: string) => void;
    safeRender?: SafeRenderFn;
}
export declare function drainAssistantStreamBoundary({ composeState, renderer, appendRendered, safeRender, }: DrainAssistantStreamBoundaryOptions): void;
type KeypressInfo = {
    name?: string;
    shift?: boolean;
    meta?: boolean;
    ctrl?: boolean;
};
export interface InputSignalState {
    continueMultiline: boolean;
    pasteStart: boolean;
    pasteEnd: boolean;
}
export interface BufferedInputSignalState extends InputSignalState {
    remainder: string;
}
export interface BufferedMouseArtifactState {
    cleaned: string;
    remainder: string;
    sawMouseSequence: boolean;
}
export interface SyntheticLineSuppression {
    until: number;
    acceptedLines: string[];
}
export interface PromptBufferRow {
    text: string;
    placeholder: boolean;
}
export declare function buildPromptBufferRows(committedLines: string[], currentLine: string, visibleRows: number): {
    rows: PromptBufferRow[];
    cursorRow: number;
};
export declare function composeBufferedInput(committedLines: string[], currentLine: string): string;
export declare function stripSgrMouseArtifacts(input: string): string;
export declare function stripBufferedMouseArtifacts(str: string, remainder?: string): BufferedMouseArtifactState;
export declare function detectInputSignals(str: string, key?: KeypressInfo): InputSignalState;
export declare function stripModifiedEnterArtifacts(line: string): string;
export declare function detectBufferedInputSignals(str: string, remainder?: string): BufferedInputSignalState;
export declare function createSyntheticLineSuppression(committedLine: string, now?: number): SyntheticLineSuppression;
export declare function shouldIgnoreSyntheticLine(line: string, suppression: SyntheticLineSuppression | null, now?: number): boolean;
export declare function shouldOpenSlashPickerOnKeypress(str: string, currentLine: string, hasActiveTask: boolean, inMultiline: boolean, pickerActive: boolean): boolean;
export declare function formatResumeCommand(sessionId: string): string;
export declare function classifyResolvedInput(input: string, hasRunningTask: boolean): 'task_ignore' | 'task_block_slash' | 'idle_empty' | 'submit';
export declare function shouldSuppressReadlineRefresh(promptDockVisible: boolean, hasRunningTask: boolean): boolean;
export declare function preflightCheck(apiBaseUrl: string): Promise<boolean>;
export declare function slashCompleter(line: string): [string[], string];
export declare const SLASH_COMMANDS_REQUIRING_ARGS: Set<string>;
export declare function buildSlashPickerItems(): PickerItem<string>[];
export type TranscriptItem = {
    id: string;
    text: string;
    overflowAnchor?: 'head' | 'tail';
};
export type TranscriptAppendChunk = {
    text: string;
    overflowAnchor?: TranscriptItem['overflowAnchor'];
};
/**
 * Split long tail-anchored assistant answers before they enter the transcript.
 *
 * A single giant transcript item cannot be partially committed to terminal
 * scrollback: when the newest item exceeds the live viewport, the tail-anchor
 * policy shows only the last N display lines and the hidden head is neither
 * visible nor Static-owned. Splitting final assistant text into line-sized
 * items gives the existing item-based Static boundary real ownership points,
 * so older answer lines can move into native scrollback instead of being
 * silently eaten by the live-window slice.
 *
 * Use DISPLAY lines rather than logical `\n` lines. A 600-column code fence
 * line is one logical line but many terminal rows; treating it as one item
 * recreates the same hidden-head bug under tail anchoring.
 */
export declare function splitTranscriptAppendText(text: string, overflowAnchor?: TranscriptItem['overflowAnchor'], width?: number): TranscriptAppendChunk[];
export interface OcWorkingIndicatorOptions {
    frame: number;
    elapsedSeconds: number;
    model: string;
    phase?: 'awaiting_model' | 'tool_execution' | 'busy';
    activeToolName?: string;
    detail?: string;
}
export declare function buildOcLoaderFrame(frame: number): string;
export declare function buildOcWorkingIndicatorLines(options: OcWorkingIndicatorOptions): string[];
export interface InlineStatusOptions {
    frame: number;
    elapsedSeconds: number;
    model: string;
    phase?: 'awaiting_model' | 'tool_execution' | 'busy';
    activeToolName?: string;
    detail?: string;
    /**
     * Milliseconds since the last concrete progress event (token arrival,
     * tool progress tick, tool start/end, retry notice, etc.). When
     * exceeded past STALL_THRESHOLD_MS, a `· idle Xs` suffix is appended
     * so the user can see that work has stopped streaming — the task may
     * still be running remotely (long tool call, slow backend), but we
     * disambiguate "still producing output" vs. "waiting silently".
     *
     * Omit or pass 0 to disable the suffix (used at task start before
     * the first progress event lands).
     */
    stallMs?: number;
}
export declare function buildInlineStatusLine(options: InlineStatusOptions): string;
export interface ScrollIndicatorOptions {
    /** Terminal width */
    cols: number;
    /** Total lines in transcript */
    totalLines: number;
    /** Lines visible in viewport */
    budgetLines: number;
    /** Current scroll offset (0 = live) */
    scrollOffset: number;
    /** Maximum scroll offset */
    maxScrollOffset: number;
    /** View end line position */
    viewEndLine: number;
    /** Is user scrolled away from live? */
    isScrolledAway: boolean;
    /** Is there more content than fits? */
    isScrollable: boolean;
    /** Is a task currently running? */
    isLoading: boolean;
    /** Number of new content items since scroll-away */
    newContentCount: number;
    /** Spinner frame for animation */
    frame: number;
}
export declare function buildScrollIndicatorBar(options: ScrollIndicatorOptions): string;
/**
 * Parse one SGR mouse wheel report from stdin data.
 * Returns +1 for wheel up, -1 for wheel down, 0 when no wheel event is present.
 *
 * Supports modifier variants by masking with 0x43, matching upstream's
 * wheel detection semantics.
 */
export declare function parseSgrWheelDelta(input: string): number;
export declare function estimateWrappedLineCount(text: string, width: number): number;
/** Test-only: clear the display-lines cache. */
export declare function __clearDisplayLinesCacheForTest(): void;
export declare function countTranscriptLines(items: TranscriptItem[], width: number): number;
export interface TranscriptOffsetsCache {
    /** Column width this cache was built for. */
    width: number;
    /** Item ids in transcript order. */
    itemIds: string[];
    /** Cumulative line count through each item (inclusive). cumulativeLines[i] = sum of lines 0..i. */
    cumulativeLines: number[];
    /** Total line count (= cumulativeLines[last] or 0 if empty). */
    totalLines: number;
}
/** Build a fresh cache for the given items + width. */
export declare function buildTranscriptOffsetsCache(items: TranscriptItem[], width: number): TranscriptOffsetsCache;
/**
 * Update an existing cache in-place for a (possibly extended) items array.
 *
 * - If `width` changed, the cache is fully rebuilt.
 * - If items are a pure append (all existing ids match), only new items are
 *   appended to the cache — O(new items).
 * - If items diverge (mutation or reorder), the cache is rebuilt from the
 *   first diverging index.
 */
export declare function updateTranscriptOffsetsCache(cache: TranscriptOffsetsCache, items: TranscriptItem[], width: number): void;
/** Return the total line count stored in the cache. */
export declare function totalLinesFromCache(cache: TranscriptOffsetsCache): number;
export declare function applyTranscriptScrollDelta(scrollOffset: number, dy: number): number;
export declare function reconcileTranscriptScrollOffset(input: {
    scrollOffset: number;
    isSticky: boolean;
    prevTotalLines: number;
    nextTotalLines: number;
    prevBudgetLines: number;
    nextBudgetLines: number;
}): number;
export declare function selectVisibleTranscriptItems(items: TranscriptItem[], width: number, maxLines: number): TranscriptItem[];
export interface TranscriptWindowResult {
    /** Items visible in the viewport */
    visible: TranscriptItem[];
    /** Total line count of entire transcript */
    totalLines: number;
    /** First visible line (0-based, from top of transcript) */
    viewStartLine: number;
    /** Last visible line (exclusive) */
    viewEndLine: number;
    /** Number of items hidden above the viewport */
    hiddenAboveCount: number;
    /** Number of items hidden below the viewport */
    hiddenBelowCount: number;
    /** Clamped scroll offset (may differ from input if over max) */
    clampedOffset: number;
    /** Maximum allowed scroll offset */
    maxScrollOffset: number;
}
export declare function selectVisibleTranscriptWindow(items: TranscriptItem[], width: number, budgetLines: number, scrollLineOffset: number, cache?: TranscriptOffsetsCache): TranscriptWindowResult;
export declare const MAX_VISIBLE_ITEMS_CAP = 200;
export interface SplitTranscriptResult<T> {
    scrollback: T[];
    visible: T[];
}
export declare function splitTranscriptForScrollback<T>(items: readonly T[], window: number): SplitTranscriptResult<T>;
export {};
