import { getPrefixedLogger } from "../../../util/logger.js";
import { LLMObsTracer } from "../../llmobs/tracer.js";
import { DEFAULT_OPTIONS } from "./config.js";
import { summarizeToolOutputs, calculatePruneableTokens, } from "./phase1-tool-summarizer.js";
import { summarizeSession } from "./phase2-session-summarizer.js";
import { captureSession } from "./session-capture.js";
import { isOverflow, getTotalTokens } from "./token-tracker.js";
const logger = getPrefixedLogger("[context-v2:compactor]");
export function needsCompaction(usage, options) {
    return isOverflow(usage, options.contextLimit, options.maxOutputTokens);
}
export async function compact(options) {
    const { messages, usage, model, config = {}, callbacks, tracer = LLMObsTracer.instance, rootDir, } = options;
    return tracer.trace({ name: "context.compaction", kind: "task" }, async (compactionSpan) => {
        const mergedOptions = {
            ...DEFAULT_OPTIONS,
            ...config,
        };
        const { contextLimit, maxOutputTokens, phase2TargetRatio, phase1Enabled, phase1MinPruneable, phase1ProtectedTokens, } = mergedOptions;
        const initialTokens = getTotalTokens(usage);
        const availableForInput = contextLimit - maxOutputTokens;
        const targetTokens = Math.floor(availableForInput * phase2TargetRatio);
        let currentMessages = messages;
        let phase1Applied = false;
        let phase2Applied = false;
        let tokensSaved = 0;
        captureSession({
            messages,
            usage,
        }, rootDir);
        const pruneableTokens = calculatePruneableTokens(messages, phase1ProtectedTokens);
        if (phase1Enabled && pruneableTokens >= phase1MinPruneable) {
            logger.info(`Phase 1: summarizing tool outputs (${pruneableTokens} pruneable tokens)`);
            const phase1MessageId = crypto.randomUUID();
            callbacks?.onCompactionStart?.("phase1", phase1MessageId);
            const result = tracer.trace({ name: "context.compaction.phase1", kind: "task" }, (phase1Span) => {
                const summarizeResult = summarizeToolOutputs({
                    messages: currentMessages,
                    protectedTokens: phase1ProtectedTokens,
                });
                const estimatedTokensAfter = initialTokens - pruneableTokens;
                tracer.annotate(phase1Span, {
                    metadata: {
                        input: {
                            messageCount: currentMessages.length,
                            pruneableTokens,
                        },
                        config: {
                            protectedTokens: phase1ProtectedTokens,
                        },
                        result: {
                            summarizedCount: summarizeResult.summarizedCount,
                            estimatedTokensAfter,
                            sufficient: estimatedTokensAfter < targetTokens,
                        },
                    },
                });
                return summarizeResult;
            });
            currentMessages = result.messages;
            phase1Applied = true;
            logger.info(`Phase 1 complete: summarized ${result.summarizedCount} tool outputs`);
            callbacks?.onCompactionComplete?.("phase1", phase1MessageId);
            // After phase 1, we can't know exact token savings without another LLM call.
            // We'll proceed to phase 2 if we still appear to be over target based on
            // the pruneable tokens we identified.
            const estimatedNewTokens = initialTokens - pruneableTokens;
            if (estimatedNewTokens < targetTokens) {
                tokensSaved = pruneableTokens;
                tracer.annotate(compactionSpan, {
                    metadata: {
                        input: {
                            messageCount: messages.length,
                            tokens: initialTokens,
                        },
                        config: {
                            contextLimit,
                            maxOutputTokens,
                            targetTokens,
                            phase1Enabled,
                            phase1MinPruneable,
                            phase1ProtectedTokens,
                            phase2TargetRatio,
                        },
                        result: {
                            tokens: initialTokens - tokensSaved,
                            tokensSaved,
                            tokensSavedIsEstimate: true,
                            phase1Applied,
                            phase2Applied,
                        },
                    },
                });
                return {
                    messages: currentMessages,
                    phase1Applied,
                    phase2Applied,
                    tokensSaved,
                };
            }
        }
        logger.info("Phase 2: generating session summary");
        const phase2MessageId = crypto.randomUUID();
        callbacks?.onCompactionStart?.("phase2", phase2MessageId);
        const summaryResult = await tracer.trace({ name: "context.compaction.phase2", kind: "task" }, async (phase2Span) => {
            const result = await summarizeSession({
                messages: currentMessages,
                model,
                targetTokens,
                tracer,
            });
            tracer.annotate(phase2Span, {
                metadata: {
                    input: {
                        messageCount: currentMessages.length,
                    },
                    result: {
                        messageCount: result.messages.length,
                        summary: result.summary,
                        summaryLength: result.summary.length,
                    },
                },
            });
            return result;
        });
        phase2Applied = true;
        currentMessages = summaryResult.messages;
        const summaryTokens = summaryResult.messages.reduce((sum, msg) => sum + (msg._superblocks?.tokenCount ?? 100), 0);
        tokensSaved = initialTokens - summaryTokens;
        logger.info(`Phase 2 complete: ${initialTokens}→${summaryTokens} tokens (saved ${tokensSaved})`);
        callbacks?.onCompactionComplete?.("phase2", phase2MessageId);
        tracer.annotate(compactionSpan, {
            metadata: {
                input: {
                    messageCount: messages.length,
                    tokens: initialTokens,
                },
                config: {
                    contextLimit,
                    maxOutputTokens,
                    targetTokens,
                    phase1Enabled,
                    phase1MinPruneable,
                    phase1ProtectedTokens,
                    phase2TargetRatio,
                },
                result: {
                    tokens: summaryTokens,
                    tokensSaved,
                    tokensSavedIsEstimate: false,
                    phase1Applied,
                    phase2Applied,
                },
            },
        });
        return {
            messages: currentMessages,
            phase1Applied,
            phase2Applied,
            tokensSaved,
        };
    });
}
//# sourceMappingURL=compactor.js.map