"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.loadSynthesisStats = loadSynthesisStats;
exports.recordSynthesisFired = recordSynthesisFired;
exports.recordSessionCompleted = recordSessionCompleted;
exports.clearSynthesisStats = clearSynthesisStats;
exports.adaptiveMaxTurns = adaptiveMaxTurns;
exports.recordAvgTurnDuration = recordAvgTurnDuration;
exports.recordToolUseStats = recordToolUseStats;
exports.adaptiveTimeBudget = adaptiveTimeBudget;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const atomicFile_1 = require("../persistence/atomicFile");
const STATS_FILE = 'synthesis-stats.json';
function statsPath(projectDir) {
    return path.join(projectDir, '.harness', STATS_FILE);
}
async function loadSynthesisStats(projectDir) {
    try {
        const raw = await fs.readFile(statsPath(projectDir), 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return {};
    }
}
async function recordSynthesisFired(projectDir, model) {
    const filePath = statsPath(projectDir);
    await (0, atomicFile_1.withFileLock)(filePath, async () => {
        const stats = await loadSynthesisStats(projectDir);
        const record = stats[model] ?? { fired: 0, total: 0 };
        record.fired++;
        record.lastFired = new Date().toISOString();
        stats[model] = record;
        await fs.mkdir(path.dirname(filePath), { recursive: true });
        await (0, atomicFile_1.atomicWriteFile)(filePath, JSON.stringify(stats, null, 2));
    });
}
async function recordSessionCompleted(projectDir, model) {
    const filePath = statsPath(projectDir);
    await (0, atomicFile_1.withFileLock)(filePath, async () => {
        const stats = await loadSynthesisStats(projectDir);
        const record = stats[model] ?? { fired: 0, total: 0 };
        record.total++;
        stats[model] = record;
        await fs.mkdir(path.dirname(filePath), { recursive: true });
        await (0, atomicFile_1.atomicWriteFile)(filePath, JSON.stringify(stats, null, 2));
    });
}
async function clearSynthesisStats(projectDir, model) {
    const filePath = statsPath(projectDir);
    await (0, atomicFile_1.withFileLock)(filePath, async () => {
        if (!model) {
            await fs.rm(filePath, { force: true });
            return;
        }
        const stats = await loadSynthesisStats(projectDir);
        delete stats[model];
        if (Object.keys(stats).length === 0) {
            await fs.rm(filePath, { force: true });
        }
        else {
            await (0, atomicFile_1.atomicWriteFile)(filePath, JSON.stringify(stats, null, 2));
        }
    });
}
/**
 * Compute an adaptive maxTurns for a model based on its synthesis history.
 * If a model triggers synthesis more than 40% of the time, it gets extra
 * turns (up to a cap). Models with no history use the default.
 */
function adaptiveMaxTurns(stats, model, defaultMax) {
    const record = stats[model];
    if (!record || record.total < 5)
        return defaultMax;
    const ratio = record.fired / record.total;
    if (ratio > 0.4)
        return Math.min(defaultMax + 10, 40);
    return defaultMax;
}
/**
 * Record the average turn duration for a model session. Uses an
 * exponential moving average (α = 0.3) so recent sessions have more
 * weight than old ones, and a single outlier doesn't dominate.
 */
async function recordAvgTurnDuration(projectDir, model, avgTurnMs) {
    if (!Number.isFinite(avgTurnMs) || avgTurnMs <= 0)
        return;
    const filePath = statsPath(projectDir);
    await (0, atomicFile_1.withFileLock)(filePath, async () => {
        const stats = await loadSynthesisStats(projectDir);
        const record = stats[model] ?? { fired: 0, total: 0 };
        const alpha = 0.3;
        record.avgTurnMs = record.avgTurnMs
            ? Math.round(record.avgTurnMs * (1 - alpha) + avgTurnMs * alpha)
            : Math.round(avgTurnMs);
        stats[model] = record;
        await fs.mkdir(path.dirname(filePath), { recursive: true });
        await (0, atomicFile_1.atomicWriteFile)(filePath, JSON.stringify(stats, null, 2));
    });
}
async function recordToolUseStats(projectDir, model, input) {
    const filePath = statsPath(projectDir);
    await (0, atomicFile_1.withFileLock)(filePath, async () => {
        const stats = await loadSynthesisStats(projectDir);
        const record = stats[model] ?? { fired: 0, total: 0 };
        const toolCalls = Math.max(0, Math.floor(input.toolCalls));
        const toolSuccesses = Math.max(0, Math.min(toolCalls, Math.floor(input.toolSuccesses)));
        record.toolCalls = (record.toolCalls ?? 0) + toolCalls;
        record.toolSuccesses = (record.toolSuccesses ?? 0) + toolSuccesses;
        if (toolCalls > 0)
            record.toolSessions = (record.toolSessions ?? 0) + 1;
        if (input.finalTextResponse)
            record.finalTextResponses = (record.finalTextResponses ?? 0) + 1;
        else
            record.emptyTextResponses = (record.emptyTextResponses ?? 0) + 1;
        if (input.parserLiftedToolCalls && input.parserLiftedToolCalls > 0) {
            record.parserLiftedToolCalls = (record.parserLiftedToolCalls ?? 0) + Math.floor(input.parserLiftedToolCalls);
        }
        stats[model] = record;
        await fs.mkdir(path.dirname(filePath), { recursive: true });
        await (0, atomicFile_1.atomicWriteFile)(filePath, JSON.stringify(stats, null, 2));
    });
}
/** Default time budgets when no per-model data exists. */
const DEFAULT_LOCAL_BUDGET_MS = 180_000;
const DEFAULT_CLOUD_BUDGET_MS = 600_000;
const MIN_BUDGET_MS = 60_000;
const MAX_BUDGET_MS = 900_000;
const TARGET_TURNS = 10;
/**
 * Compute an adaptive time budget for a model based on its measured
 * average turn duration. Targets ~TARGET_TURNS turns, clamped to
 * [MIN_BUDGET_MS, MAX_BUDGET_MS].
 *
 * Falls back to the provided default when no turn history exists.
 */
function adaptiveTimeBudget(stats, model, defaultBudgetMs) {
    const record = stats[model];
    if (!record?.avgTurnMs || record.total < 3)
        return defaultBudgetMs;
    return Math.max(MIN_BUDGET_MS, Math.min(MAX_BUDGET_MS, record.avgTurnMs * TARGET_TURNS));
}
//# sourceMappingURL=synthesisStats.js.map