"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.CodexAdapter = void 0;
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const CODEX_HOME = path.join(os.homedir(), '.codex');
const SESSIONS_DIR = path.join(CODEX_HOME, 'sessions');
const SKILLS_DIR = path.join(CODEX_HOME, 'skills');
const CONFIG_TOML = path.join(CODEX_HOME, 'config.toml');
/** Deep-cap string values inside a JSON-like value so a massive
 *  exec_command output doesn't balloon WS payloads. Mirrors claude-adapter. */
function capStrings(val, maxStrLen) {
    if (typeof val === 'string') {
        if (val.length <= maxStrLen)
            return val;
        return val.slice(0, maxStrLen) + `…[truncated ${val.length - maxStrLen} chars]`;
    }
    if (Array.isArray(val))
        return val.map((v) => capStrings(v, maxStrLen));
    if (val && typeof val === 'object') {
        const out = {};
        for (const [k, v] of Object.entries(val))
            out[k] = capStrings(v, maxStrLen);
        return out;
    }
    return val;
}
class CodexAdapter {
    constructor() {
        this.tool = 'codex';
    }
    buildCommand(permissionMode, continueSession) {
        // `codex resume --last` recovers the most recent session (equivalent to
        // Claude's --continue). Approval / sandbox flags accept positions after
        // the `resume` subcommand (verified against `codex resume --help` on 0.122).
        const base = continueSession ? 'codex resume --last' : 'codex';
        return permissionMode === 'unlimited'
            ? `${base} --ask-for-approval never --sandbox danger-full-access`
            : base;
    }
    supportsContinue() { return true; }
    /**
     * Codex stores sessions in ~/.codex/sessions/{Y}/{M}/{D}/.
     * Unlike Claude, sessions are NOT per-project — callers must scope via
     * `getSessionFilesForProject`, which matches by `cwd` from session_meta.
     */
    getSessionDir(_folderPath) {
        return fs.existsSync(SESSIONS_DIR) ? SESSIONS_DIR : null;
    }
    /**
     * Find all JSONL files for a given project folder (by matching cwd in session_meta).
     */
    getSessionFilesForProject(folderPath) {
        if (!fs.existsSync(SESSIONS_DIR))
            return [];
        const resolved = path.resolve(folderPath);
        const results = [];
        // Scan year/month/day directories
        try {
            for (const year of fs.readdirSync(SESSIONS_DIR)) {
                const yearDir = path.join(SESSIONS_DIR, year);
                if (!fs.statSync(yearDir).isDirectory())
                    continue;
                for (const month of fs.readdirSync(yearDir)) {
                    const monthDir = path.join(yearDir, month);
                    if (!fs.statSync(monthDir).isDirectory())
                        continue;
                    for (const day of fs.readdirSync(monthDir)) {
                        const dayDir = path.join(monthDir, day);
                        if (!fs.statSync(dayDir).isDirectory())
                            continue;
                        for (const file of fs.readdirSync(dayDir)) {
                            if (!file.endsWith('.jsonl'))
                                continue;
                            const filePath = path.join(dayDir, file);
                            try {
                                const firstLine = fs.readFileSync(filePath, 'utf-8').split('\n')[0];
                                const record = JSON.parse(firstLine);
                                if (record.type === 'session_meta') {
                                    const cwd = path.resolve(record.payload?.cwd || '');
                                    if (cwd === resolved || cwd.startsWith(resolved + path.sep)) {
                                        results.push(filePath);
                                    }
                                }
                            }
                            catch { /* skip unreadable */ }
                        }
                    }
                }
            }
        }
        catch { /* dir not readable */ }
        return results;
    }
    parseLineBlocks(line) {
        let record;
        try {
            record = JSON.parse(line);
        }
        catch {
            return null;
        }
        if (record.type !== 'response_item')
            return null;
        const payload = record.payload;
        if (!payload)
            return null;
        const ts = record.timestamp || '';
        // Codex real rollouts show two distinct response_item shapes:
        //
        // 1. payload.type = 'message' with role + content[{type: input_text|output_text, text}]
        // 2. payload.type = 'function_call' with name + arguments + call_id at payload top level
        //    (NOT nested inside content[] — prior versions of this adapter assumed nesting
        //     and produced zero tool_use blocks on real 0.12x sessions)
        // 3. payload.type = 'function_call_output' with output + call_id at payload top level
        // Shape (2) — tool call
        if (payload.type === 'function_call') {
            const name = payload.name || 'unknown';
            const argsRaw = payload.arguments || '';
            let parsedInput = undefined;
            try {
                parsedInput = argsRaw ? JSON.parse(argsRaw) : undefined;
            }
            catch {
                parsedInput = argsRaw;
            } // fall back to raw string if not valid JSON
            const cappedInput = capStrings(parsedInput, 4000);
            const jsonStr = cappedInput !== undefined ? JSON.stringify(cappedInput) : '';
            return {
                role: 'assistant',
                timestamp: ts,
                blocks: [{
                        type: 'tool_use',
                        // Legacy `name(args-truncated)` shape for backwards compat w/ older
                        // frontend renderers that only read .content
                        content: `${name}(${jsonStr.slice(0, 200)})`,
                        tool: name,
                        input: cappedInput,
                    }],
            };
        }
        // Shape (3) — tool result. output is always string; simpler cap.
        if (payload.type === 'function_call_output') {
            const outputRaw = payload.output || '';
            const text = outputRaw.length > 4000
                ? outputRaw.slice(0, 4000) + `…[truncated ${outputRaw.length - 4000} chars]`
                : outputRaw;
            return {
                role: 'assistant',
                timestamp: ts,
                blocks: [{ type: 'tool_result', content: text }],
            };
        }
        // Shape (1) — plain message (user / assistant text)
        if (!payload.role || !payload.content)
            return null;
        if (payload.role !== 'user' && payload.role !== 'assistant')
            return null;
        const blocks = [];
        for (const block of payload.content) {
            if (block.type === 'input_text') {
                const text = block.text || '';
                if (text.startsWith('<environment_context>') || text.startsWith('<permissions') || text.startsWith('<skills_instructions>'))
                    continue;
                if (text.trim())
                    blocks.push({ type: 'text', content: text.trim() });
            }
            else if (block.type === 'output_text') {
                const text = block.text || '';
                if (text.trim())
                    blocks.push({ type: 'text', content: text.trim() });
            }
        }
        if (blocks.length === 0)
            return null;
        return {
            role: payload.role,
            timestamp: ts,
            blocks,
        };
    }
    getHooksSettingsPath() { return null; }
    getHookEvents() { return []; }
    buildHookCommand() { return null; }
    /**
     * Read the `model = "..."` line from ~/.codex/config.toml. config.toml is
     * TOML but we only need one scalar at the top level — simple regex avoids
     * pulling in a TOML parser dep.
     */
    getCurrentModel() {
        try {
            if (!fs.existsSync(CONFIG_TOML))
                return null;
            const content = fs.readFileSync(CONFIG_TOML, 'utf-8');
            // Match top-level `model = "gpt-5.4"` — reject lines under [sections]
            // by taking only content up to the first `[` table header.
            // TOML allows both single and double quoted strings; accept either.
            const topLevel = content.split(/^\[/m)[0];
            const m = topLevel.match(/^\s*model\s*=\s*['"]([^'"]+)['"]/m);
            return m ? m[1] : null;
        }
        catch {
            return null;
        }
    }
    /** Codex 0.122's documented model families. Users can still override via
     *  `/model` slash or `-m` flag — this list just seeds the picker UI. */
    getAvailableModels() {
        return [
            { key: 'gpt-5.4', label: 'GPT-5.4 (default)' },
            { key: 'gpt-5.3', label: 'GPT-5.3' },
            { key: 'gpt-5.3-codex', label: 'GPT-5.3-codex' },
            { key: 'o3', label: 'o3' },
        ];
    }
    /**
     * Codex Skills live at `~/.codex/skills/<name>/SKILL.md` (user-installed)
     * and `~/.codex/skills/.system/<name>/SKILL.md` (bundled). Each SKILL.md
     * starts with YAML-ish frontmatter containing `name` + `description`.
     *
     * We surface them as `builtin` (.system/) and `custom` (top-level).
     * Codex doesn't have the Claude-style `~/.codex/commands/*.md` user slash
     * commands — Skills are the closest analogue.
     */
    getSkills() {
        if (!fs.existsSync(SKILLS_DIR))
            return null;
        const scan = (dir) => {
            if (!fs.existsSync(dir))
                return [];
            const out = [];
            let entries = [];
            try {
                entries = fs.readdirSync(dir);
            }
            catch {
                return [];
            }
            for (const name of entries) {
                if (name.startsWith('.'))
                    continue; // skip .system/ here; caller separates
                const skillMd = path.join(dir, name, 'SKILL.md');
                if (!fs.existsSync(skillMd))
                    continue;
                let description = '';
                try {
                    const content = fs.readFileSync(skillMd, 'utf-8');
                    // Frontmatter: `description: "..."` line. Quotes are optional in
                    // YAML but codex's bundled skills wrap in double quotes — strip
                    // matched leading/trailing ' or " so the UI doesn't show them.
                    const m = content.match(/^description:\s*(.+)$/m);
                    if (m)
                        description = m[1].trim().replace(/^["']|["']$/g, '').slice(0, 200);
                }
                catch { /* best effort */ }
                out.push({ command: `/${name}`, description });
            }
            out.sort((a, b) => a.command.localeCompare(b.command));
            return out;
        };
        return {
            builtin: scan(path.join(SKILLS_DIR, '.system')),
            custom: scan(SKILLS_DIR),
            plugins: [], // Codex plugins are managed separately via `codex marketplace`
            mcp: [],
        };
    }
    async queryUsage() { return {}; }
    getProjectInstructionsFilename() { return 'AGENTS.md'; }
    clearUsageCache() { }
}
exports.CodexAdapter = CodexAdapter;
//# sourceMappingURL=codex-adapter.js.map