// engine/agent-telemetry.ts — SPEC-691: streaming JSONL telemetry
import { appendFile, stat, readFile, writeFile, mkdir } from 'node:fs/promises';
import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';
import { join } from 'node:path';
const MAX_FILE_BYTES = 1024 * 1024; // 1 MB
const PRUNE_AGE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
const EVICT_RATIO = 0.2; // drop oldest 20%
function telemetryPath(projectPath, specId) {
    return join(projectPath, '.planu', 'team', specId, 'telemetry.jsonl');
}
async function pruneIfNeeded(file) {
    let size;
    try {
        const info = await stat(file);
        size = info.size;
    }
    catch {
        return; // file does not exist yet — nothing to prune
    }
    if (size < MAX_FILE_BYTES) {
        return;
    }
    const raw = await readFile(file, 'utf-8');
    const cutoff = Date.now() - PRUNE_AGE_MS;
    const valid = raw
        .split('\n')
        .filter((line) => line.trim().length > 0)
        .filter((line) => {
        try {
            const rec = JSON.parse(line);
            return new Date(rec.completedAt).getTime() > cutoff;
        }
        catch {
            return false;
        }
    });
    const evict = Math.floor(valid.length * EVICT_RATIO);
    const pruned = valid.slice(evict);
    await writeFile(file, pruned.join('\n') + (pruned.length > 0 ? '\n' : ''), 'utf-8');
}
/**
 * SPEC-691: Prune expired+oversized records, then append one JSON line. Never throws.
 */
export async function appendTelemetryRecord(projectPath, specId, record) {
    try {
        const file = telemetryPath(projectPath, specId);
        await mkdir(join(file, '..'), { recursive: true });
        await pruneIfNeeded(file);
        await appendFile(file, JSON.stringify(record) + '\n', 'utf-8');
    }
    catch {
        // best-effort — never throw
    }
}
/**
 * SPEC-691: Stream file line-by-line with readline, aggregate stats.
 * Never loads the full file into RAM.
 */
export async function getTelemetrySummary(projectPath, specId) {
    const empty = {
        totalAgents: 0,
        totalTokensIn: 0,
        totalTokensOut: 0,
        totalCostUsd: 0,
        avgDurationMs: 0,
        byRole: {},
        successRate: 0,
    };
    const file = telemetryPath(projectPath, specId);
    let stream;
    try {
        stream = createReadStream(file, 'utf-8');
    }
    catch {
        return empty;
    }
    const rl = createInterface({ input: stream, crlfDelay: Infinity });
    let totalAgents = 0;
    let totalTokensIn = 0;
    let totalTokensOut = 0;
    let totalCostUsd = 0;
    let totalDurationMs = 0;
    let successCount = 0;
    const byRole = {};
    try {
        for await (const line of rl) {
            if (line.trim().length === 0) {
                continue;
            }
            let rec;
            try {
                rec = JSON.parse(line);
            }
            catch {
                continue;
            }
            totalAgents++;
            totalTokensIn += rec.tokensIn;
            totalTokensOut += rec.tokensOut;
            totalCostUsd += rec.costUsd;
            totalDurationMs += rec.durationMs;
            if (rec.status === 'done') {
                successCount++;
            }
            const roleKey = rec.role;
            const existing = byRole[roleKey];
            if (existing === undefined) {
                byRole[roleKey] = { count: 1, totalCostUsd: rec.costUsd, totalDurationMs: rec.durationMs };
            }
            else {
                existing.count++;
                existing.totalCostUsd += rec.costUsd;
                existing.totalDurationMs += rec.durationMs;
            }
        }
    }
    catch {
        // stream read error — return what we have
    }
    if (totalAgents === 0) {
        return empty;
    }
    const summary = {
        totalAgents,
        totalTokensIn,
        totalTokensOut,
        totalCostUsd,
        avgDurationMs: totalDurationMs / totalAgents,
        byRole: Object.fromEntries(Object.entries(byRole).map(([role, data]) => [
            role,
            {
                count: data.count,
                totalCostUsd: data.totalCostUsd,
                avgDurationMs: data.totalDurationMs / data.count,
            },
        ])),
        successRate: successCount / totalAgents,
    };
    return summary;
}
//# sourceMappingURL=agent-telemetry.js.map