import type { Message } from '../types/messages.js'; import type { Logger } from '../types/logger.js'; import { estimateMessageTokens } from '../utils/token-estimate.js'; /** Set the module-level compaction debug logger (called by compactor constructors). */ export declare function setCompactionDebugLogger(logger: Logger | undefined): void; /** * Token estimate for a message array (text + tool I/O). Re-exported from the * canonical `token-estimate` helper so compactors and the context-pressure * monitor share one number. */ export declare const estimateMessages: typeof estimateMessageTokens; /** * Shared, pure compaction primitives. * * Before this module the three compactors (`HybridCompactor`, * `IntelligentCompactor`, `SelectiveCompactor`) each carried their own copies * of message-token estimation, tool-result elision, text detection and digest * rendering — with subtle divergences (notably Selective lacked the * tool_use/tool_result pair preservation, so it could elide the result of a * tool call it was supposed to keep). These helpers are the single source of * truth. They operate on plain `Message[]` and never touch `Context`/state, so * each compactor keeps its own `ctx.state.replaceMessages(...)` plumbing. */ /** Does this message carry any non-empty text? */ export declare function hasTextContent(m: Message): boolean; /** * Index where the preserved (recent) window starts. Walks back counting * user/assistant messages until `preserveK` are covered, then walks forward to * keep any tool_use/tool_result protocol pair intact — so a tool_result whose * tool_use is preserved is never elided. * * Instrumentation: emits `compaction.find_preserve_start.ended` with the * repair-loop block count so we can track whether protocol-pair repair is * scanning too much content. */ export declare function findPreserveStart(messages: readonly Message[], preserveK: number): number; export interface EliseResult { /** New message array, or the same reference when nothing changed. */ messages: Message[]; /** Estimated tokens reclaimed. */ saved: number; changed: boolean; } /** * Elide oversized tool I/O that falls before the preserve window. Pure: * returns a fresh array (or the same reference when unchanged). Replaces the * duplicate copies that lived in all three compactors. */ export declare function eliseOldToolResults(messages: readonly Message[], opts: { preserveK: number; eliseThreshold: number; }): EliseResult; export interface HardBudgetResult { /** New message array, or the same reference when nothing changed. */ messages: Message[]; changed: boolean; /** Estimated message tokens reclaimed (before − after). */ saved: number; /** Number of content blocks elided or truncated. */ trimmedBlocks: number; /** Number of whole messages dropped (last-resort Pass 4). */ droppedMessages: number; /** True when the message array fits `budgetTokens` after trimming. */ withinBudget: boolean; } /** * Last-resort trim that makes a request **structurally guaranteed** to fit its * budget. Call it after normal compaction when the message array still exceeds * the hard budget (a single huge paste, a preserved-window tool_result, or a * >1.5× token under-estimate). It escalates through four increasingly * destructive passes and stops the instant the array fits, so loss is * minimised: * * 1. Elide all old tool I/O before the preserve window (no threshold floor). * 2. Head/tail truncate large text in old messages. * 3. Head/tail truncate large text across the whole array (incl. preserved). * 4. Drop the oldest whole messages (never the last one) until it fits. * * A final `repairToolUseAdjacency` re-links any protocol pair Pass 4 orphaned. * * `budgetTokens` is the maximum tokens the **message array** may occupy — the * caller subtracts the system-prompt + tool-definition overhead from the * context window first, so this stays a pure function over `Message[]`. */ export declare function enforceHardBudget(messages: readonly Message[], budgetTokens: number, opts: { preserveK: number; }): HardBudgetResult; export interface DedupResult { messages: Message[]; changed: boolean; /** Estimated tokens reclaimed. */ saved: number; /** Number of stale reads collapsed. */ deduped: number; } /** * Collapse **superseded** reads of the same file. When a file is read * repeatedly (tracked in `repeatedReads` evidence), every read of it before * the preserve window that is *not* the newest occurrence is redundant — the * later read replaced it. This replaces those stale `tool_result` payloads * with a one-line marker (and elides the paired `tool_use` input), keeping the * newest read verbatim. Complements `eliseOldToolResults`, which only fires on * results ≥ `eliseThreshold`: many small-but-repeated reads slip under that bar * and accumulate, so this catches the pattern `eliseThreshold` misses. */ export declare function dedupStaleReads(messages: readonly Message[], repeatedReads: readonly { file: string; count: number; }[], opts: { preserveK: number; }): DedupResult; /** * Lossless textual digest of a message range. Every text block is kept verbatim * (across all roles, so prior `system` digests fold forward and nothing * accumulates as loss). `tool_use` / `tool_result` blocks are counted and * replaced with a marker rather than serialized — their payload is already * persisted in the session log. Empty/tool-only messages are skipped. */ export declare function buildLosslessDigest(messages: readonly Message[]): string; /** Importance score for a message — drives retention vs. summarization. */ export type ContentScore = 0 | 1 | 2 | 3 | 4 | 5; /** * Extract the plain text from a message (ignoring tool blocks). * Returns empty string if no text content exists. */ export declare function extractText(m: Message): string; /** Check if a message contains a tool_use block. */ export declare function hasToolUse(m: Message): boolean; /** Check if a message contains a tool_result block over the given char threshold. */ export declare function hasLargeToolResult(m: Message, threshold?: number): boolean; /** * Score a message by content importance. * * CRITICAL (5): user corrections, explicit "no/wrong/stop", error messages, * architecture decisions, security findings. * MEDIUM (3): normal exchanges, successful tool calls, file reads, edits. * LOW (1): large tool results (>3K chars), grep/file-list outputs, boilerplate. * NOISE (0): repeated identical failures (same tool, same error, 5th+ occurrence * within the range), pure tool I/O with no text. */ export declare function scoreMessage(m: Message, context?: { failureCounts?: Map; }): ContentScore; /** * Build a content-aware digest of messages. * * Unlike `buildLosslessDigest` which preserves all text equally, this uses * `scoreMessage` to apply tiered treatment: * - Score 5 (critical): verbatim text * - Score 3 (medium): first sentence only * - Score 1 (low): one-line summary * - Score 0 (noise): collapsed to count marker */ export declare function buildSmartDigest(messages: readonly Message[]): string; /** * Nearest safe cut boundary in [from, to]: the start of the exchange of the * closest user-with-text message. Returns -1 when no such boundary exists. */ export declare function findSafeBoundary(messages: readonly Message[], from: number, to: number): number; /** * Walk backwards from a user message to find where its logical exchange began * (just after the last assistant message that made no tool calls). */ export declare function findExchangeStart(messages: readonly Message[], userIndex: number): number; //# sourceMappingURL=compaction-core.d.ts.map