import type { LlmProvider, LlmMessage } from './provider-base.js';
export interface CompactOptions {
    /** Total char budget that triggers compaction. Default 60_000
     *  (~15k tokens for mixed EN/CN). Set via env IMHUB_NATIVE_COMPACT_TRIGGER_CHARS. */
    triggerChars?: number;
    /** Number of recent user+assistant pairs preserved verbatim.
     *  Default 6 (= 12 messages, plus any tool messages between them).
     *  Set via env IMHUB_NATIVE_COMPACT_KEEP_TURNS. */
    keepTurns?: number;
    /** v1.2.125 — number of INITIAL user messages preserved verbatim
     *  (head). Each preserved user message also carries its assistant
     *  response + any tool round-trip. Default 1 (the original goal is
     *  never summarized). 0 disables head preservation (legacy
     *  pre-v1.2.125 behaviour: only tail + summary). Set via env
     *  IMHUB_NATIVE_COMPACT_KEEP_FIRST. */
    keepFirst?: number;
    /** Provider to ask for the summary. If omitted, tries evaluator →
     *  cheap → native-chat in that order; the first registered wins. */
    summarizer?: LlmProvider;
    /** Hard cap on the summary length (chars) the summarizer is asked
     *  to produce. Default 1_500. */
    summaryCharCap?: number;
}
export interface CompactResult {
    messages: LlmMessage[];
    compacted: boolean;
    /** When compacted=true, how many of the original messages were
     *  collapsed into the synthetic summary entry. */
    collapsedCount: number;
    /** When compacted=true, char count of the summary block (so the
     *  caller can log "compacted X chars to Y chars"). */
    summaryChars: number;
    /** Char count of the original message array (sum of content). */
    originalChars: number;
}
/**
 * Pick the slice index such that messages[idx..] are the "recent" turns
 * we preserve verbatim. The slice MUST start on `role:'user'` so the
 * provider doesn't see an orphan `role:'tool'` whose matching
 * `assistant.toolCalls` was just compacted away (OpenAI / DeepSeek
 * reject the request with HTTP 400 in that case).
 *
 * Strategy: walk backwards counting user messages until we've covered
 * `keepTurns` user turns, then return the index of that user message.
 * If we couldn't find any user message, return 0 (no compaction).
 */
/**
 * v1.2.125 — pick the END index (exclusive) of the head slice such
 * that `messages[0..end)` is a clean, self-contained prefix that
 * always includes the first `keepFirstUsers` user messages plus their
 * complete assistant responses (text or tool round-trip).
 *
 * Why end on "just before the next user message": an assistant turn
 * may emit `tool_calls`; the matching `role:'tool'` messages MUST be
 * in the same slice as the assistant message or OpenAI / DeepSeek
 * reject the request (orphan tool result). Walking forward to the
 * next user message guarantees the assistant + tools are bundled.
 *
 * Returns 0 when keepFirstUsers ≤ 0 or no user message exists.
 */
export declare function pickHeadEndIndex(messages: LlmMessage[], keepFirstUsers: number): number;
/**
 * Compact if needed. Returns the (possibly new) message array plus a
 * summary metadata record. Never throws; on failure returns the input
 * unchanged with compacted=false.
 */
export declare function maybeCompactHistory(messages: LlmMessage[], opts?: CompactOptions): Promise<CompactResult>;
//# sourceMappingURL=auto-compact.d.ts.map