import type OpenAI from 'openai'; import type { Message } from './types.js'; declare function estimateTokens(messages: Message[]): number; /** * How many tool calls are still unanswered at each point in the history. * * An assistant message contributes one for every tool call it makes; each `tool` result answers * one. A cut is only safe where the count is zero — anywhere else leaves a result whose call was * summarised away, and the API rejects an orphaned tool result outright rather than degrading. * * Taken from the DeepSeek Harness, which validates compaction edges by this balance rather than * by looking at message roles. The role heuristic this replaces walked back over `tool` messages * and then returned its floor regardless — so when the walk ran out of room it produced exactly * the orphan it existed to prevent, and it could not see a batch of calls at all. */ export declare function unansweredToolCalls(messages: readonly Message[], upTo: number): number; /** * Which cut positions are balanced, in one pass. * * `balanced[i]` answers "may the history be cut immediately before message i". The whole array is * folded once rather than recomputed per candidate, which is how the harness does it: searching * backwards while recounting from zero each time is quadratic, and a long session is exactly * where compaction runs. * * A count that goes negative means a result arrived with no call to answer — corrupt history * rather than a tight squeeze — so every later position is treated as unsafe instead of the * arithmetic wrapping back to zero and offering a cut in the middle of the damage. */ export declare function toolPairingBalance(messages: readonly Message[]): boolean[]; /** Whether the history may be cut immediately before message `index`. */ export declare function toolPairingBalancedBefore(messages: readonly Message[], index: number): boolean; /** Whether the history may be cut immediately after message `index`. */ export declare function toolPairingBalancedAfter(messages: readonly Message[], index: number): boolean; /** * The latest safe cut at or before `from`, or null if there is none. * * Preferred over searching forwards because a cut is where the retained tail begins: earlier * keeps more of the recent conversation, which is the part worth keeping. */ export declare function safeCutAtOrBefore(messages: readonly Message[], from: number, min?: number, balanced?: boolean[]): number | null; /** * The earliest safe cut at or after `from`, or null if there is none. * * The fallback when nothing earlier is safe. It summarises more than asked — the tail it leaves * is shorter — but compacting slightly too much beats not compacting at all when the alternative * is a prompt the model will refuse. */ export declare function safeCutAtOrAfter(messages: readonly Message[], from: number, balanced?: boolean[]): number | null; export interface CompressOptions { /** The model's context window, so compaction happens before the model overflows rather than * at a number chosen without reference to it. */ limit: number; /** * The prompt size the provider last reported. * * Preferred over counting characters, which is a guess that ran three times low against real * Ollama. The estimate remains for the first turn, before any figure has come back. */ promptTokens?: number; /** Forced by an overflow the model has already refused: the size no longer matters. */ force?: boolean; } /** * The instruction the session is currently working on. * * Not the first one. The head of the history is pinned verbatim through every compaction, and it * holds the FIRST user message — which in a session that has been continued is a task that was * finished hours ago. The current request sits in the middle, gets summarised into a line, and the * only instruction left standing in full is the stale one. * * Reported exactly that way: a session where the earlier request had been to register and log in, * then asked to create a members page. Two compactions later the model announced it was going to * test registration and login. It was not confused; it was reading the only instruction it had * been left. */ export declare function activeRequestIndex(messages: readonly Message[]): number; /** * How many recent messages can be kept and still get under the threshold. * * Estimated from the share of the conversation each message carries, which is crude and does not * need to be otherwise: the question is only "is twelve going to be enough", and the answer is * usually a clear yes or a clear no. Never below four, because a tail shorter than that stops being * a conversation — and a compaction that leaves nothing recent is worse than one that has to run * again later. */ export declare function keepForTarget(messages: readonly Message[], size: number, limit: number, start?: number): number; export declare function compressIfNeeded(messages: Message[], client: OpenAI, model: string, options?: CompressOptions): Promise<{ messages: Message[]; compressed: boolean; }>; export { estimateTokens }; //# sourceMappingURL=compressor.d.ts.map