/**
 * Shared truncation utilities for tool outputs.
 *
 * Single limit: character count (default 20,000 chars).
 * Truncation always happens at line boundaries — never mid-line.
 */
export const DEFAULT_MAX_CHARS = 50_000;
export const GREP_MAX_LINE_LENGTH = 500; // Max chars per grep match line
export const ERROR_MAX_CHARS_UI = 200; // Max chars for error messages shown to user
export const ERROR_MAX_CHARS_LLM = 500; // Max chars for error messages sent to LLM context
/**
 * Format character count as human-readable size.
 */
export function formatSize(chars) {
    if (chars < 1000) {
        return `${chars} chars`;
    }
    else if (chars < 1_000_000) {
        return `${(chars / 1000).toFixed(1)}k chars`;
    }
    else {
        return `${(chars / 1_000_000).toFixed(1)}M chars`;
    }
}
/**
 * Truncate content from the head (keep first N chars).
 * Cuts at the last complete line boundary before maxChars.
 * If the first line alone exceeds maxChars, returns it hard-cut at maxChars.
 */
export function truncateHead(content, maxChars = DEFAULT_MAX_CHARS) {
    const totalChars = content.length;
    if (totalChars <= maxChars) {
        return { content, truncated: false, totalChars, outputChars: totalChars, maxChars };
    }
    // Find the last newline before the limit
    const cutPoint = content.lastIndexOf('\n', maxChars);
    // If no newline found (single huge line), hard-cut at maxChars
    const truncated = cutPoint > 0 ? content.slice(0, cutPoint) : content.slice(0, maxChars);
    return {
        content: truncated,
        truncated: true,
        totalChars,
        outputChars: truncated.length,
        maxChars
    };
}
/**
 * Truncate content from the tail (keep the last N chars).
 * Cuts at the first complete line boundary after the start point.
 * If no newline exists after the start point, returns the last maxChars chars.
 */
export function truncateTail(content, maxChars = DEFAULT_MAX_CHARS) {
    const totalChars = content.length;
    if (totalChars <= maxChars) {
        return { content, truncated: false, totalChars, outputChars: totalChars, maxChars };
    }
    const startIndex = Math.max(0, totalChars - maxChars);
    const cutPoint = content.indexOf('\n', startIndex);
    const truncated = cutPoint >= 0 ? content.slice(cutPoint + 1) : content.slice(startIndex);
    return {
        content: truncated,
        truncated: true,
        totalChars,
        outputChars: truncated.length,
        maxChars
    };
}
/**
 * Truncate content and return a single string with a notice appended if truncated.
 * This is the main entry point for tool outputs going back to the LLM.
 *
 * Returns content as-is if no truncation needed, or:
 *   <truncated content>\n\n[Truncated: showing 5.0k of 50.0k chars]
 */
export function truncateOutput(content, maxChars = DEFAULT_MAX_CHARS) {
    const result = truncateHead(content, maxChars);
    if (!result.truncated)
        return content;
    return `${result.content}\n\n[Truncated: showing ${formatSize(result.outputChars)} of ${formatSize(result.totalChars)}]`;
}
/**
 * Truncate long errors by keeping both the start and the end.
 * This preserves traceback endings where the exception type/message usually lives.
 */
export function truncateErrorOutput(content, maxChars = ERROR_MAX_CHARS_LLM) {
    if (content.length <= maxChars)
        return content;
    const headBudget = Math.max(80, Math.floor(maxChars * 0.35));
    const tailBudget = Math.max(80, maxChars - headBudget);
    const head = truncateHead(content, headBudget);
    const tail = truncateTail(content, tailBudget);
    if (!head.truncated || !tail.truncated) {
        return content;
    }
    const omittedChars = Math.max(0, content.length - head.outputChars - tail.outputChars);
    return `${head.content}\n\n[... truncated ${formatSize(omittedChars)} ...]\n\n${tail.content}`;
}
/**
 * Truncate a single line to max characters, adding [truncated] suffix.
 * Used for grep match lines.
 */
export function truncateLine(line, maxChars = GREP_MAX_LINE_LENGTH) {
    if (line.length <= maxChars) {
        return { text: line, wasTruncated: false };
    }
    return { text: `${line.slice(0, maxChars)}... [truncated]`, wasTruncated: true };
}
//# sourceMappingURL=truncate.js.map