"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFileReadTool = void 0;
const ai_1 = require("ai");
const toolDefinitions_1 = require("../../utils/tools/toolDefinitions");
const fileCommon_1 = require("./fileCommon");
const Runtime_1 = require("../../runtime/Runtime");
const helpers_1 = require("../../utils/runtime/helpers");
/**
 * File read tool factory for AI assistant
 * Creates a tool that allows the AI to read file contents from the file system
 * @param config Required configuration including working directory
 */
const createFileReadTool = (config) => {
    return (0, ai_1.tool)({
        description: toolDefinitions_1.TOOL_DEFINITIONS.file_read.description,
        inputSchema: toolDefinitions_1.TOOL_DEFINITIONS.file_read.schema,
        execute: async ({ filePath, offset, limit }, { abortSignal: _abortSignal }) => {
            // Note: abortSignal available but not used - file reads are fast and complete quickly
            try {
                // Validate no redundant path prefix (must come first to catch absolute paths)
                const redundantPrefixValidation = (0, fileCommon_1.validateNoRedundantPrefix)(filePath, config.cwd, config.runtime);
                if (redundantPrefixValidation) {
                    return {
                        success: false,
                        error: redundantPrefixValidation.error,
                    };
                }
                // Validate that the path is within the working directory
                const pathValidation = (0, fileCommon_1.validatePathInCwd)(filePath, config.cwd, config.runtime);
                if (pathValidation) {
                    return {
                        success: false,
                        error: pathValidation.error,
                    };
                }
                // Use runtime's normalizePath method to resolve paths correctly for both local and SSH runtimes
                const resolvedPath = config.runtime.normalizePath(filePath, config.cwd);
                // Check if file exists using runtime
                let fileStat;
                try {
                    fileStat = await config.runtime.stat(resolvedPath);
                }
                catch (err) {
                    if (err instanceof Runtime_1.RuntimeError) {
                        return {
                            success: false,
                            error: err.message,
                        };
                    }
                    throw err;
                }
                if (fileStat.isDirectory) {
                    return {
                        success: false,
                        error: `Path is a directory, not a file: ${resolvedPath}`,
                    };
                }
                // Validate file size
                const sizeValidation = (0, fileCommon_1.validateFileSize)(fileStat);
                if (sizeValidation) {
                    return {
                        success: false,
                        error: sizeValidation.error,
                    };
                }
                // Read full file content using runtime helper
                let fullContent;
                try {
                    fullContent = await (0, helpers_1.readFileString)(config.runtime, resolvedPath);
                }
                catch (err) {
                    if (err instanceof Runtime_1.RuntimeError) {
                        return {
                            success: false,
                            error: err.message,
                        };
                    }
                    throw err;
                }
                const startLineNumber = offset ?? 1;
                // Validate offset
                if (offset !== undefined && offset < 1) {
                    return {
                        success: false,
                        error: `Offset must be positive (got ${offset})`,
                    };
                }
                // Split content into lines for processing
                // Handle empty file case: splitting "" by "\n" gives [""], but we want []
                const lines = fullContent === "" ? [] : fullContent.split("\n");
                // Validate offset
                if (offset !== undefined && offset > lines.length) {
                    return {
                        success: false,
                        error: `Offset ${offset} is beyond file length`,
                    };
                }
                const numberedLines = [];
                let totalBytesAccumulated = 0;
                const MAX_LINE_BYTES = 1024;
                const MAX_LINES = 1000;
                const MAX_TOTAL_BYTES = 16 * 1024; // 16KB
                // Process lines with offset and limit
                const startIdx = startLineNumber - 1; // Convert to 0-based index
                const endIdx = limit !== undefined ? startIdx + limit : lines.length;
                for (let i = startIdx; i < Math.min(endIdx, lines.length); i++) {
                    const line = lines[i];
                    const lineNumber = i + 1;
                    // Truncate line if it exceeds max bytes
                    let processedLine = line;
                    const lineBytes = Buffer.byteLength(line, "utf-8");
                    if (lineBytes > MAX_LINE_BYTES) {
                        // Truncate to MAX_LINE_BYTES
                        processedLine = Buffer.from(line, "utf-8")
                            .subarray(0, MAX_LINE_BYTES)
                            .toString("utf-8");
                        processedLine += "... [truncated]";
                    }
                    // Format line with number prefix
                    const numberedLine = `${lineNumber}\t${processedLine}`;
                    const numberedLineBytes = Buffer.byteLength(numberedLine, "utf-8");
                    // Check if adding this line would exceed byte limit
                    if (totalBytesAccumulated + numberedLineBytes > MAX_TOTAL_BYTES) {
                        return {
                            success: false,
                            error: `Output would exceed ${MAX_TOTAL_BYTES} bytes. Please read less at a time using offset and limit parameters.`,
                        };
                    }
                    numberedLines.push(numberedLine);
                    totalBytesAccumulated += numberedLineBytes + 1; // +1 for newline
                    // Check if we've exceeded max lines
                    if (numberedLines.length > MAX_LINES) {
                        return {
                            success: false,
                            error: `Output would exceed ${MAX_LINES} lines. Please read less at a time using offset and limit parameters.`,
                        };
                    }
                }
                // Join lines with newlines
                const content = numberedLines.join("\n");
                // Return file info and content
                return {
                    success: true,
                    file_size: fileStat.size,
                    modifiedTime: fileStat.modifiedTime.toISOString(),
                    lines_read: numberedLines.length,
                    content,
                };
            }
            catch (error) {
                // Handle specific errors
                if (error && typeof error === "object" && "code" in error) {
                    if (error.code === "ENOENT") {
                        return {
                            success: false,
                            error: `File not found: ${filePath}`,
                        };
                    }
                    else if (error.code === "EACCES") {
                        return {
                            success: false,
                            error: `Permission denied: ${filePath}`,
                        };
                    }
                }
                // Generic error
                const message = error instanceof Error ? error.message : String(error);
                return {
                    success: false,
                    error: `Failed to read file: ${message}`,
                };
            }
        },
    });
};
exports.createFileReadTool = createFileReadTool;
//# sourceMappingURL=file_read.js.map