"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskExecutor = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const child_process = __importStar(require("child_process"));
const generative_ai_1 = require("@google/generative-ai");
const ToolsetGuard_1 = require("./toolset/ToolsetGuard");
const ProcessSandbox_1 = require("./ProcessSandbox");
class TaskExecutor {
    artifactsDir;
    commandTimeoutMs = 10 * 60 * 1000;
    constructor() {
        this.artifactsDir = path.join(process.cwd(), '.artifacts');
        if (!fs.existsSync(this.artifactsDir)) {
            fs.mkdirSync(this.artifactsDir, { recursive: true });
        }
    }
    async execute(kind, prompt, projectName) {
        switch (kind) {
            case 'gemini':
                return this.executeWithGemini(prompt, projectName);
            case 'claude':
                return this.executeWithClaude(prompt, projectName, 'claude');
            case 'openclaude':
                return this.executeWithClaude(prompt, projectName, 'openclaude');
            default:
                return this.executeWithCodex(prompt, projectName);
        }
    }
    async executeWithCodex(prompt, projectName) {
        (0, ToolsetGuard_1.assertToolsetAllowed)('delegation', 'TaskExecutor.executeWithCodex');
        const artifactDir = this.ensureArtifactDir(projectName);
        const cwd = process.cwd();
        const outputFile = path.join(artifactDir, 'codex-last-message.txt');
        const stdoutFile = path.join(artifactDir, 'codex-stdout.jsonl');
        const stderrFile = path.join(artifactDir, 'codex-stderr.txt');
        const shellCommand = `cd ${JSON.stringify(cwd)} && codex exec --skip-git-repo-check --model gpt-5.4 --json --output-last-message ${JSON.stringify(outputFile)} ${JSON.stringify(prompt)} > ${JSON.stringify(stdoutFile)} 2> ${JSON.stringify(stderrFile)}`;
        const startedAt = new Date().toISOString();
        const startedMs = Date.now();
        const result = await this.runShellCommand(shellCommand, cwd);
        const finishedAt = new Date().toISOString();
        const durationMs = Date.now() - startedMs;
        const stdout = this.safeRead(stdoutFile);
        const stderr = this.safeRead(stderrFile);
        const finalMessage = fs.existsSync(outputFile) ? this.safeRead(outputFile).trim() : this.extractCodexFinalMessage(stdout);
        if (result.code === 0 && finalMessage) {
            const artifactPath = path.join(artifactDir, 'codex-result.txt');
            fs.writeFileSync(artifactPath, finalMessage, 'utf-8');
            const maskedStdout = this.maskSensitiveText(finalMessage);
            const maskedStderr = this.maskSensitiveText(stderr);
            const metadataPath = this.writeExecutionMetadata(artifactDir, {
                executor: 'codex', ok: true, status: 'success', fallbackUsed: false, providerUnavailable: false, initRan: false,
                projectName, artifactPath, stdout: maskedStdout, stderr: maskedStderr, command: ['/bin/bash', '-lc', shellCommand],
                commandProvenance: shellCommand,
                exitCode: result.code, signal: result.signal, startedAt, finishedAt, durationMs
            });
            return {
                executor: 'codex',
                ok: true,
                projectName,
                artifactPath,
                stdout: maskedStdout,
                stderr: maskedStderr,
                command: ['/bin/bash', '-lc', shellCommand],
                commandProvenance: shellCommand,
                exitCode: result.code,
                signal: result.signal,
                startedAt,
                finishedAt,
                durationMs,
                metadataPath,
                status: 'success',
                fallbackUsed: false,
                providerUnavailable: false,
                initRan: false
            };
        }
        const artifactPath = path.join(artifactDir, 'codex-error.txt');
        fs.writeFileSync(artifactPath, `${finalMessage || stdout}\n\n${stderr}`, 'utf-8');
        const maskedStdout = this.maskSensitiveText(finalMessage || stdout);
        const maskedStderr = this.maskSensitiveText(stderr);
        const metadataPath = this.writeExecutionMetadata(artifactDir, {
            executor: 'codex', ok: false, status: 'failed', fallbackUsed: false, providerUnavailable: false, initRan: false,
            projectName, artifactPath, stdout: maskedStdout, stderr: maskedStderr, command: ['/bin/bash', '-lc', shellCommand],
            commandProvenance: shellCommand,
            exitCode: result.code, signal: result.signal, startedAt, finishedAt, durationMs
        });
        return {
            executor: 'codex',
            ok: false,
            projectName,
            artifactPath,
            stdout: maskedStdout,
            stderr: maskedStderr,
            command: ['/bin/bash', '-lc', shellCommand],
            commandProvenance: shellCommand,
            exitCode: result.code,
            signal: result.signal,
            startedAt,
            finishedAt,
            durationMs,
            metadataPath,
            status: 'failed',
            fallbackUsed: false,
            providerUnavailable: false,
            initRan: false
        };
    }
    async executeWithGemini(prompt, projectName) {
        (0, ToolsetGuard_1.assertToolsetAllowed)('delegation', 'TaskExecutor.executeWithGemini');
        const artifactDir = this.ensureArtifactDir(projectName);
        const cwd = process.cwd();
        const stdoutFile = path.join(artifactDir, 'gemini-stdout.txt');
        const stderrFile = path.join(artifactDir, 'gemini-stderr.txt');
        const resultFile = path.join(artifactDir, 'gemini-result.txt');
        const geminiApiKey = (process.env.GEMINI_API_KEY || '').trim();
        if (geminiApiKey) {
            const startedAt = new Date().toISOString();
            const startedMs = Date.now();
            const artifactPath = resultFile;
            try {
                const modelName = process.env.OPENLIFE_TASK_GEMINI_MODEL || process.env.OPENLIFE_GEMINI_MODEL || 'gemini-3.1-flash-lite-preview';
                const api = new generative_ai_1.GoogleGenerativeAI(geminiApiKey);
                const model = api.getGenerativeModel({ model: modelName });
                const result = await model.generateContent(prompt);
                const response = await result.response;
                const finalMessage = response.text().trim();
                const finishedAt = new Date().toISOString();
                const durationMs = Date.now() - startedMs;
                if (!finalMessage)
                    throw new Error('EMPTY_GEMINI_API_RESPONSE');
                fs.writeFileSync(artifactPath, finalMessage, 'utf-8');
                const maskedStdout = this.maskSensitiveText(finalMessage);
                const metadataPath = this.writeExecutionMetadata(artifactDir, {
                    executor: 'gemini', ok: true, status: 'success', fallbackUsed: false, providerUnavailable: false, initRan: false,
                    projectName, artifactPath, stdout: maskedStdout, stderr: '', command: ['gemini-api', modelName],
                    commandProvenance: `gemini-api/${modelName}`,
                    exitCode: 0, signal: null, startedAt, finishedAt, durationMs
                });
                return {
                    executor: 'gemini', ok: true, projectName, artifactPath, stdout: maskedStdout, stderr: '',
                    command: ['gemini-api', modelName], commandProvenance: `gemini-api/${modelName}`,
                    exitCode: 0, signal: null, startedAt, finishedAt, durationMs, metadataPath,
                    status: 'success', fallbackUsed: false, providerUnavailable: false, initRan: false
                };
            }
            catch (err) {
                const finishedAt = new Date().toISOString();
                const durationMs = Date.now() - startedMs;
                const message = err instanceof Error ? err.message : String(err);
                const errorPath = path.join(artifactDir, 'gemini-api-error.txt');
                fs.writeFileSync(errorPath, message, 'utf-8');
                const providerUnavailable = /quota|429|capacity|rate limit|temporarily unavailable|overloaded/i.test(message);
                const maskedStderr = this.maskSensitiveText(message);
                const metadataPath = this.writeExecutionMetadata(artifactDir, {
                    executor: 'gemini', ok: false, status: providerUnavailable ? 'partial' : 'failed', fallbackUsed: false, providerUnavailable, initRan: false,
                    projectName, artifactPath: errorPath, stdout: '', stderr: maskedStderr, command: ['gemini-api'],
                    commandProvenance: 'gemini-api', exitCode: 1, signal: null, startedAt, finishedAt, durationMs
                });
                return {
                    executor: 'gemini', ok: false, projectName, artifactPath: errorPath, stdout: '', stderr: maskedStderr,
                    command: ['gemini-api'], commandProvenance: 'gemini-api', exitCode: 1, signal: null,
                    startedAt, finishedAt, durationMs, metadataPath,
                    status: providerUnavailable ? 'partial' : 'failed', fallbackUsed: false, providerUnavailable, initRan: false
                };
            }
        }
        const shellCommand = `cd ${JSON.stringify(cwd)} && gemini -o json -m gemini-2.5-pro -p ${JSON.stringify(prompt)} -y > ${JSON.stringify(stdoutFile)} 2> ${JSON.stringify(stderrFile)}`;
        const startedAt = new Date().toISOString();
        const startedMs = Date.now();
        const result = await this.runShellCommand(shellCommand, cwd);
        const finishedAt = new Date().toISOString();
        const durationMs = Date.now() - startedMs;
        const stdout = this.safeRead(stdoutFile);
        const stderr = this.safeRead(stderrFile);
        const finalMessage = this.extractGeminiFinalMessage(stdout);
        if (result.code === 0 && finalMessage) {
            fs.writeFileSync(resultFile, finalMessage, 'utf-8');
            const maskedStdout = this.maskSensitiveText(finalMessage);
            const maskedStderr = this.maskSensitiveText(stderr);
            const metadataPath = this.writeExecutionMetadata(artifactDir, {
                executor: 'gemini', ok: true, status: 'success', fallbackUsed: false, providerUnavailable: false, initRan: false,
                projectName, artifactPath: resultFile, stdout: maskedStdout, stderr: maskedStderr, command: ['/bin/bash', '-lc', shellCommand],
                commandProvenance: shellCommand,
                exitCode: result.code, signal: result.signal, startedAt, finishedAt, durationMs
            });
            return {
                executor: 'gemini',
                ok: true,
                projectName,
                artifactPath: resultFile,
                stdout: maskedStdout,
                stderr: maskedStderr,
                command: ['/bin/bash', '-lc', shellCommand],
                commandProvenance: shellCommand,
                exitCode: result.code,
                signal: result.signal,
                startedAt,
                finishedAt,
                durationMs,
                metadataPath,
                status: 'success',
                fallbackUsed: false,
                providerUnavailable: false,
                initRan: false
            };
        }
        const artifactPath = path.join(artifactDir, 'gemini-error.txt');
        fs.writeFileSync(artifactPath, `${stdout}\n\n${stderr}`, 'utf-8');
        const providerUnavailable = /quota_exhausted|status 429|exhausted your capacity|retry with backoff/i.test(stderr);
        const maskedStdout = this.maskSensitiveText(stdout);
        const maskedStderr = this.maskSensitiveText(stderr);
        const metadataPath = this.writeExecutionMetadata(artifactDir, {
            executor: 'gemini', ok: false, status: providerUnavailable ? 'partial' : 'failed', fallbackUsed: false, providerUnavailable, initRan: false,
            projectName, artifactPath, stdout: maskedStdout, stderr: maskedStderr, command: ['/bin/bash', '-lc', shellCommand],
            commandProvenance: shellCommand,
            exitCode: result.code, signal: result.signal, startedAt, finishedAt, durationMs
        });
        return {
            executor: 'gemini',
            ok: false,
            projectName,
            artifactPath,
            stdout: maskedStdout,
            stderr: maskedStderr,
            command: ['/bin/bash', '-lc', shellCommand],
            commandProvenance: shellCommand,
            exitCode: result.code,
            signal: result.signal,
            startedAt,
            finishedAt,
            durationMs,
            metadataPath,
            status: providerUnavailable ? 'partial' : 'failed',
            fallbackUsed: false,
            providerUnavailable,
            initRan: false
        };
    }
    async executeWithClaude(prompt, projectName, cliName) {
        const artifactDir = this.ensureArtifactDir(projectName);
        const cwd = process.cwd();
        const stdoutFile = path.join(artifactDir, `${cliName}-stdout.txt`);
        const stderrFile = path.join(artifactDir, `${cliName}-stderr.txt`);
        const resultFile = path.join(artifactDir, `${cliName}-result.txt`);
        const initMetaFile = path.join(artifactDir, `${cliName}-init-meta.json`);
        const initInstruction = this.buildClaudeInitInstruction(projectName);
        const initCommand = this.buildClaudeInitCommand(cwd, cliName, initInstruction, stdoutFile, stderrFile);
        const shouldInit = !this.hasClaudeInit(cwd);
        if (shouldInit) {
            const initResult = await this.runShellCommand(initCommand, cwd);
            fs.writeFileSync(initMetaFile, JSON.stringify({ initialized: initResult.code === 0, cliName, cwd }, null, 2), 'utf-8');
        }
        const shellCommand = this.buildClaudeExecCommand(cwd, cliName, prompt, stdoutFile, stderrFile);
        const startedAt = new Date().toISOString();
        const startedMs = Date.now();
        const result = await this.runShellCommand(shellCommand, cwd);
        const finishedAt = new Date().toISOString();
        const durationMs = Date.now() - startedMs;
        const stdout = this.safeRead(stdoutFile);
        const stderr = this.safeRead(stderrFile);
        const finalMessage = stdout.trim();
        if (result.code === 0 && finalMessage) {
            fs.writeFileSync(resultFile, finalMessage, 'utf-8');
            const maskedStdout = this.maskSensitiveText(finalMessage);
            const maskedStderr = this.maskSensitiveText(stderr);
            const metadataPath = this.writeExecutionMetadata(artifactDir, {
                executor: cliName, ok: true, status: 'success', fallbackUsed: false, providerUnavailable: false, initRan: shouldInit,
                projectName, artifactPath: resultFile, stdout: maskedStdout, stderr: maskedStderr, command: ['/bin/bash', '-lc', shellCommand],
                commandProvenance: shellCommand,
                exitCode: result.code, signal: result.signal, startedAt, finishedAt, durationMs
            });
            return {
                executor: cliName,
                ok: true,
                projectName,
                artifactPath: resultFile,
                stdout: maskedStdout,
                stderr: maskedStderr,
                command: ['/bin/bash', '-lc', shellCommand],
                commandProvenance: shellCommand,
                exitCode: result.code,
                signal: result.signal,
                startedAt,
                finishedAt,
                durationMs,
                metadataPath,
                status: 'success',
                fallbackUsed: false,
                providerUnavailable: false,
                initRan: shouldInit
            };
        }
        const artifactPath = path.join(artifactDir, `${cliName}-error.txt`);
        fs.writeFileSync(artifactPath, `${stdout}\n\n${stderr}`, 'utf-8');
        const maskedStdout = this.maskSensitiveText(stdout);
        const maskedStderr = this.maskSensitiveText(stderr);
        const metadataPath = this.writeExecutionMetadata(artifactDir, {
            executor: cliName, ok: false, status: 'failed', fallbackUsed: false, providerUnavailable: false, initRan: shouldInit,
            projectName, artifactPath, stdout: maskedStdout, stderr: maskedStderr, command: ['/bin/bash', '-lc', shellCommand],
            commandProvenance: shellCommand,
            exitCode: result.code, signal: result.signal, startedAt, finishedAt, durationMs
        });
        return {
            executor: cliName,
            ok: false,
            projectName,
            artifactPath,
            stdout: maskedStdout,
            stderr: maskedStderr,
            command: ['/bin/bash', '-lc', shellCommand],
            commandProvenance: shellCommand,
            exitCode: result.code,
            signal: result.signal,
            startedAt,
            finishedAt,
            durationMs,
            metadataPath,
            status: 'failed',
            fallbackUsed: false,
            providerUnavailable: false,
            initRan: shouldInit
        };
    }
    ensureArtifactDir(projectName) {
        const artifactDir = path.join(this.artifactsDir, projectName);
        if (!fs.existsSync(artifactDir)) {
            fs.mkdirSync(artifactDir, { recursive: true });
        }
        return artifactDir;
    }
    safeRead(filePath) {
        return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : '';
    }
    async runShellCommand(shellCommand, cwd) {
        (0, ToolsetGuard_1.assertToolsetAllowed)('terminal', 'TaskExecutor.runShellCommand');
        // Story 18.2 (v1.6) — opt-in route through ProcessSandbox so the spawn
        // path is consistent across the codebase and produces a structured
        // telemetry envelope (enforced state, applied flags, captured stdout).
        // For /bin/bash this is currently a pass-through — Node's --permission
        // only applies when spawning Node itself — but the wiring is in place
        // for the v1.7 work that will route node-script steps through the
        // wrapper with real enforcement.
        if ((process.env.OPENLIFE_PROCESS_SANDBOX || '').toLowerCase() === 'on') {
            const sandbox = new ProcessSandbox_1.ProcessSandbox({ allowFsRead: [cwd], allowFsWrite: [path.join(cwd, '.artifacts')] });
            const result = await sandbox.spawn('/bin/bash', ['-lc', shellCommand], { cwd, timeout: this.commandTimeoutMs });
            return { code: result.code, signal: result.signal };
        }
        return new Promise((resolve) => {
            const child = child_process.spawn('/bin/bash', ['-lc', shellCommand], {
                cwd,
                stdio: 'ignore'
            });
            const timer = setTimeout(() => {
                child.kill('SIGTERM');
            }, this.commandTimeoutMs);
            child.on('close', (code, signal) => {
                clearTimeout(timer);
                resolve({ code, signal });
            });
            child.on('error', () => {
                clearTimeout(timer);
                resolve({ code: 1, signal: null });
            });
        });
    }
    hasClaudeInit(cwd) {
        return fs.existsSync(path.join(cwd, 'CLAUDE.md')) || fs.existsSync(path.join(cwd, 'Claude.md'));
    }
    buildClaudeInitInstruction(projectName) {
        return [
            '/init',
            '',
            `Projeto: ${projectName}`,
            'Gere o contexto inicial do projeto neste repositório.',
            'Inclua também regras úteis de execução, arquitetura e comportamento do projeto.',
            'Se este projeto já tiver Claude.md/CLAUDE.md, não regenere do zero desnecessariamente.'
        ].join('\n');
    }
    buildClaudeInitCommand(cwd, cliName, prompt, stdoutFile, stderrFile) {
        if (cliName === 'claude') {
            return `cd ${JSON.stringify(cwd)} && claude --dangerously-skip-permissions --print ${JSON.stringify(prompt)} > ${JSON.stringify(stdoutFile)} 2> ${JSON.stringify(stderrFile)}`;
        }
        return `cd ${JSON.stringify(cwd)} && openclaude ${JSON.stringify(prompt)} > ${JSON.stringify(stdoutFile)} 2> ${JSON.stringify(stderrFile)}`;
    }
    buildClaudeExecCommand(cwd, cliName, prompt, stdoutFile, stderrFile) {
        if (cliName === 'claude') {
            return `cd ${JSON.stringify(cwd)} && claude --dangerously-skip-permissions --print ${JSON.stringify(prompt)} > ${JSON.stringify(stdoutFile)} 2> ${JSON.stringify(stderrFile)}`;
        }
        return `cd ${JSON.stringify(cwd)} && openclaude ${JSON.stringify(prompt)} > ${JSON.stringify(stdoutFile)} 2> ${JSON.stringify(stderrFile)}`;
    }
    writeExecutionMetadata(artifactDir, payload) {
        const metadataPath = path.join(artifactDir, 'execution-meta.json');
        fs.writeFileSync(metadataPath, JSON.stringify(payload, null, 2), 'utf-8');
        return metadataPath;
    }
    extractCodexFinalMessage(stdoutJsonl) {
        const lines = stdoutJsonl.split('\n').map(l => l.trim()).filter(Boolean);
        for (let i = lines.length - 1; i >= 0; i--) {
            try {
                const parsed = JSON.parse(lines[i]);
                if (typeof parsed?.item?.text === 'string' && parsed.item.text.trim())
                    return parsed.item.text.trim();
                if (typeof parsed?.message === 'string' && parsed.message.trim())
                    return parsed.message.trim();
                if (typeof parsed?.content === 'string' && parsed.content.trim())
                    return parsed.content.trim();
            }
            catch {
                continue;
            }
        }
        return '';
    }
    extractGeminiFinalMessage(stdout) {
        const trimmed = stdout.trim();
        if (!trimmed)
            return '';
        const jsonStart = trimmed.indexOf('{');
        if (jsonStart !== -1) {
            try {
                const parsed = JSON.parse(trimmed.slice(jsonStart));
                if (typeof parsed?.response === 'string' && parsed.response.trim())
                    return parsed.response.trim();
                if (typeof parsed?.text === 'string' && parsed.text.trim())
                    return parsed.text.trim();
            }
            catch {
                // fallback abaixo
            }
        }
        return trimmed;
    }
    maskSensitiveText(text) {
        return text
            .replace(/\b(sk|ghp|sbp|hf|fc|rnd|vcp|gsk|sm)_[A-Za-z0-9\-_]+\b/g, '[REDACTED_TOKEN]')
            .replace(/\bAIza[0-9A-Za-z\-_]{20,}\b/g, '[REDACTED_GOOGLE_KEY]')
            .replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, '[REDACTED_EMAIL]');
    }
}
exports.TaskExecutor = TaskExecutor;
