"use strict";
// =============================================================================
// Pure Agent - Utility Functions
// Ported from Hermes utils.py
// =============================================================================
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.atomicJsonWrite = atomicJsonWrite;
exports.atomicWrite = atomicWrite;
exports.safeJsonParse = safeJsonParse;
exports.estimateTokens = estimateTokens;
exports.estimateMessagesTokens = estimateMessagesTokens;
exports.resolvePath = resolvePath;
exports.normalizePath = normalizePath;
exports.truncate = truncate;
exports.getTextContent = getTextContent;
exports.sanitizeError = sanitizeError;
exports.getBaseUrlHostname = getBaseUrlHostname;
exports.getBaseUrlHostMatches = getBaseUrlHostMatches;
exports.detectApiMode = detectApiMode;
exports.jitteredBackoff = jitteredBackoff;
exports.sleep = sleep;
exports.getPlatformHint = getPlatformHint;
exports.generateId = generateId;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const constants_1 = require("./constants");
// ---------------------------------------------------------------------------
// Atomic file writes (crash-safe)
// ---------------------------------------------------------------------------
function atomicJsonWrite(filePath, data, indent = 2) {
    const dir = path.dirname(filePath);
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
    }
    const tmpPath = filePath + '.tmp.' + process.pid;
    try {
        const json = JSON.stringify(data, null, indent);
        fs.writeFileSync(tmpPath, json, 'utf-8');
        fs.renameSync(tmpPath, filePath);
    }
    catch (err) {
        try {
            fs.unlinkSync(tmpPath);
        }
        catch { /* ignore cleanup error */ }
        throw err;
    }
}
function atomicWrite(filePath, content) {
    const dir = path.dirname(filePath);
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
    }
    const tmpPath = filePath + '.tmp.' + process.pid;
    try {
        fs.writeFileSync(tmpPath, content, 'utf-8');
        fs.renameSync(tmpPath, filePath);
    }
    catch (err) {
        try {
            fs.unlinkSync(tmpPath);
        }
        catch { /* ignore cleanup error */ }
        throw err;
    }
}
// ---------------------------------------------------------------------------
// JSON helpers
// ---------------------------------------------------------------------------
function safeJsonParse(text, defaultValue = null) {
    try {
        return JSON.parse(text);
    }
    catch {
        return defaultValue;
    }
}
// ---------------------------------------------------------------------------
// Token estimation
// ---------------------------------------------------------------------------
function estimateTokens(text) {
    return Math.ceil(text.length / constants_1.CHARS_PER_TOKEN_ESTIMATE);
}
function estimateMessagesTokens(messages) {
    let total = 0;
    for (const msg of messages) {
        total += 4; // role overhead
        if (msg.content)
            total += estimateTokens(getTextContent(msg.content));
        if (msg.tool_calls) {
            for (const tc of msg.tool_calls) {
                total += estimateTokens(tc.function.name) + estimateTokens(tc.function.arguments);
            }
        }
    }
    return total;
}
// ---------------------------------------------------------------------------
// Path helpers
// ---------------------------------------------------------------------------
function resolvePath(filepath, cwd) {
    if (path.isAbsolute(filepath))
        return filepath;
    return path.resolve(cwd || process.cwd(), filepath);
}
function normalizePath(filepath) {
    return path.normalize(filepath).replace(/\\/g, '/');
}
// ---------------------------------------------------------------------------
// String helpers
// ---------------------------------------------------------------------------
function truncate(text, maxChars) {
    if (text.length <= maxChars)
        return { text, truncated: false };
    const headRatio = 0.7;
    const tailRatio = 0.2;
    const headLen = Math.floor(maxChars * headRatio);
    const tailLen = Math.floor(maxChars * tailRatio);
    const head = text.slice(0, headLen);
    const tail = text.slice(-tailLen);
    const omitted = text.length - headLen - tailLen;
    return {
        text: `${head}\n\n... [${omitted} characters omitted] ...\n\n${tail}`,
        truncated: true,
    };
}
/**
 * Extract text content from a message that may have string or array content.
 * Array content (multimodal) is flattened to text parts only.
 */
function getTextContent(content) {
    if (content === null || content === undefined)
        return '';
    if (typeof content === 'string')
        return content;
    if (Array.isArray(content)) {
        return content
            .filter((b) => b.type === 'text' && b.text)
            .map((b) => b.text)
            .join('\n');
    }
    return String(content);
}
function sanitizeError(error) {
    if (typeof error === 'string')
        return error;
    if (error?.message)
        return error.message;
    if (error?.error?.message)
        return error.error.message;
    return String(error);
}
// ---------------------------------------------------------------------------
// URL helpers
// ---------------------------------------------------------------------------
function getBaseUrlHostname(baseUrl) {
    try {
        return new URL(baseUrl).hostname;
    }
    catch {
        return '';
    }
}
function getBaseUrlHostMatches(baseUrl, domain) {
    const hostname = getBaseUrlHostname(baseUrl);
    if (!hostname)
        return false;
    return hostname === domain || hostname.endsWith('.' + domain);
}
function detectApiMode(provider, baseUrl) {
    const hostname = getBaseUrlHostname(baseUrl);
    const providerLower = provider.toLowerCase();
    // Anthropic native
    if (providerLower === 'anthropic' || hostname === 'api.anthropic.com') {
        return 'anthropic_messages';
    }
    // URL ends with /anthropic (third-party compat)
    if (baseUrl.endsWith('/anthropic')) {
        return 'anthropic_messages';
    }
    // Codex / Responses API
    if (providerLower === 'openai-codex' ||
        providerLower === 'xai' ||
        providerLower === 'xai-oauth' ||
        hostname === 'chatgpt.com') {
        return 'codex_responses';
    }
    // Default: OpenAI-compatible Chat Completions
    return 'chat_completions';
}
// ---------------------------------------------------------------------------
// Jittered backoff delay
// ---------------------------------------------------------------------------
function jitteredBackoff(attempt, baseDelayMs, maxDelayMs) {
    const exponential = baseDelayMs * Math.pow(2, attempt);
    const capped = Math.min(exponential, maxDelayMs);
    const jitter = capped * (0.5 + Math.random() * 0.5);
    return Math.floor(jitter);
}
// ---------------------------------------------------------------------------
// Sleep with abort
// ---------------------------------------------------------------------------
function sleep(ms, signal) {
    return new Promise((resolve, reject) => {
        if (signal?.aborted) {
            reject(new Error('Aborted'));
            return;
        }
        const timer = setTimeout(resolve, ms);
        if (signal) {
            const onAbort = () => {
                clearTimeout(timer);
                reject(new Error('Aborted'));
            };
            signal.addEventListener('abort', onAbort, { once: true });
        }
    });
}
// ---------------------------------------------------------------------------
// Platform info
// ---------------------------------------------------------------------------
function getPlatformHint() {
    const platform = os.platform();
    const arch = os.arch();
    const nodeVersion = process.version;
    const cwd = process.cwd();
    return [
        `Platform: ${platform} (${arch})`,
        `Node.js: ${nodeVersion}`,
        `Working directory: ${cwd}`,
        `Shell: ${process.env.SHELL || process.env.COMSPEC || 'unknown'}`,
    ].join('\n');
}
// ---------------------------------------------------------------------------
// Counter ID generator
// ---------------------------------------------------------------------------
let _idCounter = 0;
function generateId(prefix = 'pa') {
    _idCounter++;
    const ts = Date.now().toString(36);
    const counter = _idCounter.toString(36);
    return `${prefix}_${ts}_${counter}`;
}
//# sourceMappingURL=utils.js.map