"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var HistoryCompressionService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.HistoryCompressionService = void 0;
const common_1 = require("@nestjs/common");
const application_state_service_1 = require("../application-state/application-state.service");
let HistoryCompressionService = HistoryCompressionService_1 = class HistoryCompressionService {
    constructor(applicationStateService) {
        this.applicationStateService = applicationStateService;
        this.logger = new common_1.Logger(HistoryCompressionService_1.name);
        this.FILE_MARKER_REGEX = /\/\/ File: (\.?[^\n]+)/g;
        this.FILE_CONTENT_TOOLS = [
            'create_file',
            'overwrite_file',
            'delete_file',
        ];
        this.FILE_WRITE_TOOLS = ['create_file', 'overwrite_file'];
    }
    async compress(history) {
        const enabled = await this.applicationStateService.getHistoryCompressionEnabled();
        if (!enabled) {
            return history;
        }
        if (history.length === 0) {
            return history;
        }
        const fileIndex = this.buildUnifiedFileIndex(history);
        if (fileIndex.size === 0) {
            return history;
        }
        return this.redactBasedOnIndex(history, fileIndex);
    }
    buildUnifiedFileIndex(history) {
        const fileIndex = new Map();
        const toolCallInfo = new Map();
        for (let i = 0; i < history.length; i++) {
            const turn = history[i];
            if (turn.role === 'model' && turn.tool_calls) {
                for (const tc of turn.tool_calls) {
                    const toolName = tc.function?.name;
                    if (toolName === 'request_context' ||
                        this.isFileContentTool(toolName)) {
                        toolCallInfo.set(tc.id, { historyIndex: i, toolName });
                    }
                }
            }
        }
        const toolResultIndex = new Map();
        for (let i = 0; i < history.length; i++) {
            const turn = history[i];
            if (turn.role === 'tool' && turn.tool_call_id) {
                toolResultIndex.set(turn.tool_call_id, i);
            }
        }
        for (let i = history.length - 1; i >= 0; i--) {
            const turn = history[i];
            if (turn.role === 'tool' && turn.tool_call_id) {
                const info = toolCallInfo.get(turn.tool_call_id);
                if (info && info.toolName === 'request_context') {
                    const text = turn.parts.map((p) => p.text).join('');
                    const filePaths = this.extractFilePaths(text);
                    for (const filePath of filePaths) {
                        const normalized = this.normalizePath(filePath);
                        if (!fileIndex.has(normalized)) {
                            fileIndex.set(normalized, {
                                latestIndex: i,
                                latestToolName: 'request_context',
                            });
                        }
                    }
                }
            }
            if (turn.role === 'model' && turn.tool_calls) {
                for (let j = turn.tool_calls.length - 1; j >= 0; j--) {
                    const tc = turn.tool_calls[j];
                    const toolName = tc.function?.name;
                    if (this.isFileContentTool(toolName)) {
                        const filePath = this.extractFilePathFromToolCall(tc);
                        if (filePath) {
                            const normalized = this.normalizePath(filePath);
                            const effectiveIndex = toolResultIndex.get(tc.id) ?? i;
                            if (!fileIndex.has(normalized) ||
                                effectiveIndex > fileIndex.get(normalized).latestIndex) {
                                fileIndex.set(normalized, {
                                    latestIndex: effectiveIndex,
                                    latestToolName: toolName,
                                });
                            }
                        }
                    }
                }
            }
        }
        return fileIndex;
    }
    redactBasedOnIndex(history, fileIndex) {
        const toolCallToIndex = new Map();
        for (let i = 0; i < history.length; i++) {
            const turn = history[i];
            if (turn.role === 'model' && turn.tool_calls) {
                for (const tc of turn.tool_calls) {
                    if (tc.function?.name === 'request_context') {
                        toolCallToIndex.set(tc.id, i);
                    }
                }
            }
        }
        const toolResultIndex = new Map();
        for (let i = 0; i < history.length; i++) {
            const turn = history[i];
            if (turn.role === 'tool' && turn.tool_call_id) {
                toolResultIndex.set(turn.tool_call_id, i);
            }
        }
        let redactedResults = 0;
        let redactedToolCalls = 0;
        const result = history.map((turn, index) => {
            if (turn.role === 'tool' && turn.tool_call_id) {
                const modelTurnIndex = toolCallToIndex.get(turn.tool_call_id);
                if (modelTurnIndex !== undefined) {
                    const text = turn.parts.map((p) => p.text).join('');
                    const filePaths = this.extractFilePaths(text);
                    if (filePaths.length > 0) {
                        const filesToRedact = [];
                        for (const filePath of filePaths) {
                            const normalized = this.normalizePath(filePath);
                            const versionInfo = fileIndex.get(normalized);
                            if (versionInfo && versionInfo.latestIndex > index) {
                                filesToRedact.push({
                                    filePath,
                                    latestToolName: versionInfo.latestToolName,
                                });
                            }
                        }
                        if (filesToRedact.length > 0) {
                            redactedResults++;
                            return this.redactToolResult(turn, filesToRedact);
                        }
                    }
                }
            }
            if (turn.role === 'model' && turn.tool_calls) {
                let hasRedaction = false;
                const newToolCalls = turn.tool_calls.map((tc) => {
                    const toolName = tc.function?.name;
                    if (!this.isFileWriteTool(toolName)) {
                        return tc;
                    }
                    const filePath = this.extractFilePathFromToolCall(tc);
                    if (!filePath) {
                        return tc;
                    }
                    const normalized = this.normalizePath(filePath);
                    const versionInfo = fileIndex.get(normalized);
                    const effectiveIndex = toolResultIndex.get(tc.id) ?? index;
                    if (versionInfo && versionInfo.latestIndex > effectiveIndex) {
                        hasRedaction = true;
                        redactedToolCalls++;
                        return this.redactToolCallContent(tc, filePath, versionInfo.latestToolName);
                    }
                    return tc;
                });
                if (hasRedaction) {
                    return {
                        ...turn,
                        tool_calls: newToolCalls,
                    };
                }
            }
            return turn;
        });
        if (redactedResults > 0 || redactedToolCalls > 0) {
            const parts = [];
            if (redactedResults > 0) {
                parts.push(`${redactedResults} older tool result(s)`);
            }
            if (redactedToolCalls > 0) {
                parts.push(`${redactedToolCalls} older tool call(s)`);
            }
            this.logger.log(`Compressed history: redacted ${parts.join(' and ')}`);
        }
        return result;
    }
    isFileContentTool(toolName) {
        return !!toolName && this.FILE_CONTENT_TOOLS.includes(toolName);
    }
    isFileWriteTool(toolName) {
        return !!toolName && this.FILE_WRITE_TOOLS.includes(toolName);
    }
    extractFilePathFromToolCall(toolCall) {
        const args = toolCall.function?.arguments;
        if (!args)
            return null;
        let parsedArgs = args;
        if (typeof args === 'string') {
            try {
                parsedArgs = JSON.parse(args);
            }
            catch {
                return null;
            }
        }
        return parsedArgs.file_path || null;
    }
    redactToolCallContent(toolCall, filePath, latestToolName) {
        const args = toolCall.function?.arguments;
        let parsedArgs = args;
        if (typeof args === 'string') {
            try {
                parsedArgs = JSON.parse(args);
            }
            catch {
                return toolCall;
            }
        }
        const redactedArgs = {
            ...parsedArgs,
            content: `[Content for ${filePath} redacted - see newer ${latestToolName} for this file]`,
        };
        return {
            ...toolCall,
            function: {
                ...toolCall.function,
                arguments: typeof args === 'string'
                    ? JSON.stringify(redactedArgs)
                    : redactedArgs,
            },
        };
    }
    extractFilePaths(text) {
        const paths = [];
        let match;
        this.FILE_MARKER_REGEX.lastIndex = 0;
        while ((match = this.FILE_MARKER_REGEX.exec(text)) !== null) {
            paths.push(match[1]);
        }
        return paths;
    }
    normalizePath(path) {
        let normalized = path.replace(/^\.?\/*/, '');
        normalized = normalized.replace(/\/+$/, '');
        return normalized;
    }
    parseFileBlocks(text) {
        const blocks = [];
        const parts = text.split(/(?=\/\/ File: )/g);
        for (const part of parts) {
            if (!part.trim())
                continue;
            const firstLineEnd = part.indexOf('\n');
            if (firstLineEnd === -1) {
                const match = part.match(/\/\/ File: (.+)/);
                if (match) {
                    blocks.push({ filePath: match[1].trim(), content: part });
                }
                continue;
            }
            const firstLine = part.substring(0, firstLineEnd);
            const match = firstLine.match(/\/\/ File: (.+)/);
            if (match) {
                blocks.push({
                    filePath: match[1].trim(),
                    content: part,
                });
            }
        }
        return blocks;
    }
    redactToolResult(turn, filesToRedact) {
        const text = turn.parts.map((p) => p.text).join('');
        const blocks = this.parseFileBlocks(text);
        const redactionInfo = new Map();
        for (const f of filesToRedact) {
            redactionInfo.set(this.normalizePath(f.filePath), f.latestToolName);
        }
        const normalizedFilesToRedact = new Set(redactionInfo.keys());
        const resultParts = [];
        for (const block of blocks) {
            const normalizedBlockPath = this.normalizePath(block.filePath);
            if (normalizedFilesToRedact.has(normalizedBlockPath)) {
                const latestToolName = redactionInfo.get(normalizedBlockPath);
                resultParts.push(`[Content for ${block.filePath} redacted - see newer ${latestToolName}]`);
            }
            else {
                resultParts.push(block.content);
            }
        }
        if (resultParts.length === 0) {
            const fileList = filesToRedact.length === 1
                ? filesToRedact[0].filePath
                : `${filesToRedact.length} files`;
            const latestToolName = filesToRedact[0].latestToolName;
            return {
                ...turn,
                parts: [
                    {
                        text: `[Content for ${fileList} redacted - see newer ${latestToolName}]`,
                    },
                ],
            };
        }
        return {
            ...turn,
            parts: [{ text: resultParts.join('\n\n') }],
        };
    }
};
exports.HistoryCompressionService = HistoryCompressionService;
exports.HistoryCompressionService = HistoryCompressionService = HistoryCompressionService_1 = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [application_state_service_1.ApplicationStateService])
], HistoryCompressionService);
//# sourceMappingURL=history-compression.service.js.map