"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 __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GetMessagesHandler = exports.CONTEXT_FLOOR_TOKENS = exports.CONTEXT_TARGET_TOKENS = exports.DEFAULT_CHARS_PER_TOKEN = void 0;
exports.computeTurnCharSize = computeTurnCharSize;
exports.buildHistory = buildHistory;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const core_entities_1 = require("../../core-entities");
const history_compression_service_1 = require("../history-compression.service");
const utils_1 = require("../../utils");
const PREVIEW_MAX_LENGTH = 120;
exports.DEFAULT_CHARS_PER_TOKEN = 4;
exports.CONTEXT_TARGET_TOKENS = 50000;
exports.CONTEXT_FLOOR_TOKENS = 30000;
function truncate(text, maxLen = PREVIEW_MAX_LENGTH) {
    if (!text)
        return '';
    const cleaned = text.replace(/\n/g, ' ').trim();
    if (cleaned.length <= maxLen)
        return cleaned;
    return cleaned.slice(0, maxLen - 3) + '...';
}
function buildActionPreview(action) {
    switch (action.action_type) {
        case 'create_file':
            return `create_file: ${action.file_path || ''}`;
        case 'overwrite_file':
            return `overwrite_file: ${action.file_path || ''}`;
        case 'delete_file':
            return `delete_file: ${action.file_path || ''}`;
        case 'patch':
            return `patch: ${action.file_path || ''}`;
        case 'quick_edit':
            return `quick_edit: ${action.file_path || ''}`;
        case 'apply_diff':
            return `apply_diff: ${action.file_path || ''}`;
        case 'run_command':
            return `run_command: ${truncate(action.command_string || '')}`;
        case 'request_context':
            return `request_context: ${truncate(action.files || action.folders || '')}`;
        case 'execute_code':
            return `execute_code: ${truncate(action.command_string || '')}`;
        case 'invoke_subagent':
            return `invoke_subagent: ${truncate(action.arguments || '')}`;
        case 'list_sub_agents':
            return 'list_sub_agents';
        case 'new_session':
            return `new_session: ${truncate(action.handover_string || '')}`;
        case 'get_session_history':
            return 'get_session_history';
        case 'generate_title':
            return 'generate_title';
        case 'ask_user':
            return `ask_user: ${truncate(action.plain || '')}`;
        case 'write_todo':
            return `write_todo: ${action.file_path || ''}`;
        case 'get_messages':
            return 'get_messages';
        case 'discard_messages':
            return 'discard_messages';
        case 'final':
            if (action.selections) {
                return `final: user selection`;
            }
            return `final: ${truncate(action.plain || '')}`;
        default:
            if (action.action_type.includes('__')) {
                return action.action_type;
            }
            return `${action.action_type}: ${truncate(action.file_path || action.command_string || action.plain || '')}`;
    }
}
function computeTurnCharSize(turn) {
    let size = turn.parts.map((p) => p.text).join('').length;
    if (turn.thoughts) {
        size += turn.thoughts.length;
    }
    if (turn.tool_calls) {
        size += JSON.stringify(turn.tool_calls).length;
    }
    return size;
}
function buildHistory(inputs) {
    const history = [];
    const turnToInputIndex = [];
    for (let inputIdx = 0; inputIdx < inputs.length; inputIdx++) {
        const input = inputs[inputIdx];
        if (input.role === 'user' && input.generated_context_string) {
            history.push({
                role: 'user',
                parts: [{ text: input.generated_context_string }],
            });
            turnToInputIndex.push(inputIdx);
        }
        else if (input.role === 'model') {
            const content = {
                role: 'model',
                parts: [{ text: input.raw_llm_response || '' }],
            };
            if (input.thoughts) {
                content.thoughts = input.thoughts;
            }
            if (input.tool_calls) {
                try {
                    content.tool_calls = JSON.parse(input.tool_calls);
                }
                catch (_e) {
                }
            }
            history.push(content);
            turnToInputIndex.push(inputIdx);
            if (content.tool_calls && content.tool_calls.length > 0) {
                const actionsByToolCallId = new Map();
                if (input.aiActions) {
                    for (const action of input.aiActions) {
                        if (action.tool_call_id) {
                            actionsByToolCallId.set(action.tool_call_id, action);
                        }
                    }
                }
                for (const toolCall of content.tool_calls) {
                    const action = actionsByToolCallId.get(toolCall.id);
                    if (!action)
                        continue;
                    const output = action.executionLogs?.[0]?.output || '';
                    const errorMessage = action.executionLogs?.[0]?.error_message || '';
                    let toolResult;
                    if (output && errorMessage) {
                        toolResult = `${output}\n\nError: ${errorMessage}`;
                    }
                    else if (errorMessage) {
                        toolResult = `Error: ${errorMessage}`;
                    }
                    else if (output) {
                        toolResult = output;
                    }
                    else {
                        const statusText = action.status || 'unknown';
                        toolResult = `Tool ${action.action_type} executed with status: ${statusText}. No output available.`;
                    }
                    history.push({
                        role: 'tool',
                        tool_call_id: toolCall.id,
                        tool_name: action.action_type,
                        parts: [{ text: toolResult }],
                    });
                    turnToInputIndex.push(inputIdx);
                }
            }
        }
    }
    return { history, turnToInputIndex };
}
let GetMessagesHandler = class GetMessagesHandler {
    constructor(sessionInputsRepository, historyCompressionService) {
        this.sessionInputsRepository = sessionInputsRepository;
        this.historyCompressionService = historyCompressionService;
        this.toolName = 'get_messages';
    }
    getMetadata() {
        return {
            name: this.toolName,
            description: 'Get a summary of non-discarded messages in the current session, grouped by topic. Returns groups with labels, message counts, estimated tokens, and per-message details. Use this to identify completed work to discard from context.',
            arguments: [],
        };
    }
    getDefinition() {
        return `## get_messages

Get a summary of non-discarded messages in the current session, organized into logical groups. Returns groups with topic labels, message counts, estimated tokens, and individual message details. Use this to identify messages to discard from the LLM context window.

### Parameters:
No parameters required. Uses the current session context.

### Returns:
JSON object with:
- \`total_est_tokens\`: Sum of all estimated token counts across all messages
- \`target_tokens\`: Target token count after discarding (${exports.CONTEXT_TARGET_TOKENS}). Discard until near this value.
- \`floor_tokens\`: Minimum token count (${exports.CONTEXT_FLOOR_TOKENS}). **Never discard below this value.**
- \`groups\`: Array of logical groups, each with:
  - \`label\`: Topic label derived from the first user message in the group (e.g., "Fix auth bug", "Explore database schema")
  - \`label_suffix\`: " (ongoing)" for the most recent group, empty otherwise
  - \`message_count\`: Number of messages in this group
  - \`est_tokens\`: Total estimated tokens for this group
  - \`messages\`: Array of message summaries, each with:
    - \`short_id\`: Compact ID (first 3 + last 2 chars of UUID, e.g. "a1b90") — use this with \`discard_messages\`
    - \`role\`: "user" or "model"
    - \`preview\`: brief description (user prompt text or tool name + args)
    - \`est_tokens\`: estimated token count for this message

### Token Estimation:
Token counts are estimated by:
1. Reconstructing chat history from session inputs (same format sent to LLM)
2. Applying history compression (redacting older file contents)
3. Computing \`char_size\` from compressed history for each message
4. Deriving a \`char/token ratio\` from the last model input with a known \`input_token_count\` (cumulative tokens)
5. Using a fallback ratio of 4 chars/token if no token count is available
6. \`est_tokens = round(char_size / ratio)\`

### Example:
\`\`\`typescript
tool: get_messages
args: {}
\`\`\``;
    }
    async execute(_args, context) {
        try {
            const inputs = await this.sessionInputsRepository.find({
                where: { session: { id: context.session_id }, is_discarded: false },
                relations: ['aiActions', 'aiActions.executionLogs'],
                order: { sequence_number: 'ASC' },
            });
            const { history, turnToInputIndex } = buildHistory(inputs);
            const compressedHistory = await this.historyCompressionService.compress(history);
            const compressedCharSizes = new Array(inputs.length).fill(0);
            for (let i = 0; i < compressedHistory.length; i++) {
                const turnCharSize = computeTurnCharSize(compressedHistory[i]);
                const inputIdx = turnToInputIndex[i];
                if (inputIdx >= 0 && inputIdx < inputs.length) {
                    compressedCharSizes[inputIdx] += turnCharSize;
                }
            }
            let ratio = exports.DEFAULT_CHARS_PER_TOKEN;
            let ratioInputIndex = -1;
            for (let i = inputs.length - 1; i >= 0; i--) {
                if (inputs[i].role === 'model' &&
                    inputs[i].input_token_count != null &&
                    inputs[i].input_token_count > 0) {
                    ratioInputIndex = i;
                    break;
                }
            }
            if (ratioInputIndex >= 0) {
                let cumulativeChars = 0;
                for (let i = 0; i <= ratioInputIndex; i++) {
                    cumulativeChars += compressedCharSizes[i];
                }
                if (cumulativeChars > 0) {
                    ratio = cumulativeChars / inputs[ratioInputIndex].input_token_count;
                }
            }
            const summaries = inputs.map((input, index) => {
                let preview;
                if (input.role === 'user') {
                    preview = truncate(input.user_prompt || '');
                }
                else if (input.aiActions && input.aiActions.length > 0) {
                    preview = input.aiActions
                        .sort((a, b) => a.order_of_execution - b.order_of_execution)
                        .map((a) => buildActionPreview(a))
                        .join(', ');
                    if (preview.length > PREVIEW_MAX_LENGTH) {
                        preview = preview.slice(0, PREVIEW_MAX_LENGTH - 3) + '...';
                    }
                }
                else if (input.error_message) {
                    preview = `error: ${truncate(input.error_message)}`;
                }
                else if (input.llm_response_explanation) {
                    preview = truncate(input.llm_response_explanation);
                }
                else {
                    preview = '(empty response)';
                }
                const estTokens = compressedCharSizes[index] > 0
                    ? Math.round(compressedCharSizes[index] / ratio)
                    : 0;
                return {
                    short_id: (0, utils_1.toShortId)(input.id),
                    role: input.role || 'user',
                    preview,
                    est_tokens: estTokens,
                };
            });
            const groups = [];
            let currentGroup = [];
            let currentLabel = '';
            let currentGroupTokens = 0;
            for (let i = 0; i < summaries.length; i++) {
                const summary = summaries[i];
                if (summary.role === 'user' && currentGroup.length > 0) {
                    groups.push({
                        label: currentLabel,
                        label_suffix: '',
                        message_count: currentGroup.length,
                        est_tokens: currentGroupTokens,
                        messages: currentGroup,
                    });
                    currentGroup = [];
                    currentGroupTokens = 0;
                }
                if (summary.role === 'user' || currentGroup.length === 0) {
                    currentLabel = summary.preview || 'User message';
                }
                currentGroup.push(summary);
                currentGroupTokens += summary.est_tokens;
            }
            if (currentGroup.length > 0) {
                const isOngoing = true;
                groups.push({
                    label: currentLabel,
                    label_suffix: isOngoing ? ' (ongoing)' : '',
                    message_count: currentGroup.length,
                    est_tokens: currentGroupTokens,
                    messages: currentGroup,
                });
            }
            for (let i = 0; i < groups.length - 1; i++) {
                groups[i].label_suffix = '';
            }
            const totalEstTokens = summaries.reduce((sum, s) => sum + s.est_tokens, 0);
            return {
                status: 'SUCCESS',
                summary: `${summaries.length} messages in ${groups.length} groups, ~${totalEstTokens} est tokens`,
                execution_log: {
                    output: JSON.stringify({
                        total_est_tokens: totalEstTokens,
                        target_tokens: exports.CONTEXT_TARGET_TOKENS,
                        floor_tokens: exports.CONTEXT_FLOOR_TOKENS,
                        groups,
                    }, null, 2),
                },
                persisted_args: {},
            };
        }
        catch (error) {
            return {
                status: 'FAILURE',
                summary: 'Failed to get messages',
                error_message: error.message,
                execution_log: {
                    output: '',
                    error_message: error.message,
                },
            };
        }
    }
};
exports.GetMessagesHandler = GetMessagesHandler;
exports.GetMessagesHandler = GetMessagesHandler = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(core_entities_1.SessionInput)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        history_compression_service_1.HistoryCompressionService])
], GetMessagesHandler);
//# sourceMappingURL=get-messages.handler.js.map