export interface CompressionResult { originalInput: string; input: string; applied: boolean; method: string; tokensBefore: number; tokensAfter: number; tokensSaved: number; ratio: number; strategy: string; tags: string[]; notes: string[]; } interface CompressionOptions { enabled?: boolean; mode?: string; targetTokens?: number; } function estimateTokens(text: string): number { return Math.max(1, Math.ceil(text.length / 4)); } function noCompression(input: string, method = 'none:none'): CompressionResult { const tokens = estimateTokens(input); return { originalInput: input, input, applied: false, method, tokensBefore: tokens, tokensAfter: tokens, tokensSaved: 0, ratio: 0, strategy: 'none', tags: [], notes: ['compression not applied'], }; } export function compressContext(input: string, options: CompressionOptions = {}): CompressionResult { if (options.enabled === false || input.trim().length === 0) return noCompression(input); const tokensBefore = estimateTokens(input); const targetTokens = options.targetTokens ?? (tokensBefore > 12_000 ? 6_000 : 0); if (!targetTokens || tokensBefore <= targetTokens) return noCompression(input); const targetChars = Math.max(2_000, targetTokens * 4); const headChars = Math.floor(targetChars * 0.62); const tailChars = Math.max(800, targetChars - headChars); const omittedChars = Math.max(0, input.length - headChars - tailChars); const compacted = [ input.slice(0, headChars).trimEnd(), '', `[ctxlean omitted ${omittedChars} chars from the middle; preserve exact visible context]`, '', input.slice(-tailChars).trimStart(), ].join('\n'); const tokensAfter = estimateTokens(compacted); return { originalInput: input, input: compacted, applied: true, method: `ctxlean:${options.mode ?? 'auto'}`, tokensBefore, tokensAfter, tokensSaved: Math.max(0, tokensBefore - tokensAfter), ratio: tokensBefore > 0 ? Math.max(0, 1 - tokensAfter / tokensBefore) : 0, strategy: 'head-tail-excerpt', tags: ['subscription-bridge-safe', 'lossy-excerpt'], notes: ['long context compacted before model routing'], }; }