"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
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.METRICS_DIR = exports.AgentMetricsCollector = void 0;
exports.persistSessionMetrics = persistSessionMetrics;
exports.cleanupOldMetrics = cleanupOldMetrics;
/**
 * Agent Metrics Collector
 *
 * Collects tool execution timing and SDK usage metrics per analysis session.
 * Designed as a lightweight, non-intrusive layer:
 * - Tool timing: collected via wrapToolHandler() at the tool() factory level
 * - SDK usage: recorded from sdkQuery result messages (if available)
 *
 * Data flow:
 *   tool handler → wrapToolHandler → timing recorded → original result returned
 *   sdkQuery result → recordSdkTurnInfo → turn count recorded
 *
 * Output:
 *   summarize() → SessionMetrics object → written to logs/metrics/ by ClaudeRuntime
 *
 * Design decisions (from Codex review):
 * - Tool payload measured in chars, NOT estimated tokens (avoid tokenizer mismatch)
 * - SDK model usage NOT estimated (only recorded if SDK explicitly provides it)
 * - Decorator wraps the handler function, not injected inside each tool definition
 */
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const runtimePaths_1 = require("../runtimePaths");
// =============================================================================
// AgentMetricsCollector
// =============================================================================
class AgentMetricsCollector {
    constructor(sessionId) {
        this.toolExecutions = [];
        this.turnCount = 0;
        this.cacheMetrics = null;
        this.turnMetricsSummary = null;
        this.analysisMode = null;
        this.classifierSource = null;
        this.sessionId = sessionId;
        this.startTime = Date.now();
    }
    /**
     * Wrap a tool handler function with timing instrumentation.
     * Returns a new function with the same signature that records execution metrics.
     */
    wrapToolHandler(toolName, handler) {
        return async (input) => {
            const start = Date.now();
            const inputChars = safeStringifyLength(input);
            try {
                const result = await handler(input);
                this.toolExecutions.push({
                    toolName,
                    startTime: start,
                    durationMs: Date.now() - start,
                    inputChars,
                    outputChars: safeStringifyLength(result),
                    success: true,
                });
                return result;
            }
            catch (err) {
                this.toolExecutions.push({
                    toolName,
                    startTime: start,
                    durationMs: Date.now() - start,
                    inputChars,
                    outputChars: 0,
                    success: false,
                    error: err.message?.substring(0, 200),
                });
                throw err;
            }
        };
    }
    /**
     * Record a tool execution observed from the SDK stream.
     * Timing = time between tool_use (assistant message) and tool_use_result (user message).
     * Less precise than in-handler timing but requires no modification to tool definitions.
     */
    recordToolFromStream(toolName, durationMs, success) {
        this.toolExecutions.push({
            toolName,
            startTime: Date.now() - durationMs,
            durationMs,
            inputChars: 0, // Not available from stream observation
            outputChars: 0, // Not available from stream observation
            success,
        });
    }
    /** Record a turn completion from SDK stream processing. */
    recordTurn() {
        this.turnCount++;
    }
    /**
     * Record SDK result usage metrics for prompt cache analysis.
     * Called once per analysis when the SDK result message arrives.
     */
    recordSdkUsage(result) {
        const usage = result.usage;
        if (!usage)
            return;
        const inputTokens = (usage.input_tokens ?? 0);
        const outputTokens = (usage.output_tokens ?? 0);
        const cacheCreation = (usage.cache_creation_input_tokens ?? 0);
        const cacheRead = (usage.cache_read_input_tokens ?? 0);
        const totalInput = inputTokens + cacheCreation + cacheRead;
        this.cacheMetrics = {
            inputTokens,
            outputTokens,
            cacheCreationInputTokens: cacheCreation,
            cacheReadInputTokens: cacheRead,
            totalCostUsd: (result.total_cost_usd ?? 0),
            cacheHitRate: totalInput > 0 ? cacheRead / totalInput : 0,
        };
        // Log cache effectiveness for monitoring
        const hitPct = (this.cacheMetrics.cacheHitRate * 100).toFixed(1);
        console.log(`[AgentMetrics] [${this.sessionId}] Prompt cache: ` +
            `${cacheRead} read / ${cacheCreation} created / ${inputTokens} uncached ` +
            `(hit rate: ${hitPct}%, cost: $${this.cacheMetrics.totalCostUsd.toFixed(4)})`);
    }
    /** Record per-turn metrics collected from SDK stream processing. */
    recordTurnMetrics(summary) {
        this.turnMetricsSummary = summary;
    }
    /** Record the analysis mode (explicit from UI/CLI or inferred by classifier). */
    recordAnalysisMode(mode, source) {
        this.analysisMode = mode;
        this.classifierSource = source;
    }
    /** Generate session metrics summary. */
    summarize() {
        const endTime = Date.now();
        const byTool = {};
        for (const exec of this.toolExecutions) {
            if (!byTool[exec.toolName]) {
                byTool[exec.toolName] = { calls: 0, totalMs: 0, avgMs: 0, failures: 0 };
            }
            const entry = byTool[exec.toolName];
            entry.calls++;
            entry.totalMs += exec.durationMs;
            if (!exec.success)
                entry.failures++;
        }
        for (const entry of Object.values(byTool)) {
            entry.avgMs = entry.calls > 0 ? Math.round(entry.totalMs / entry.calls) : 0;
        }
        const successCount = this.toolExecutions.filter(e => e.success).length;
        const totalToolMs = this.toolExecutions.reduce((sum, e) => sum + e.durationMs, 0);
        return {
            sessionId: this.sessionId,
            startTime: this.startTime,
            endTime,
            totalDurationMs: endTime - this.startTime,
            turns: this.turnCount,
            toolExecutions: this.toolExecutions,
            toolSummary: {
                totalCalls: this.toolExecutions.length,
                totalDurationMs: totalToolMs,
                successCount,
                failureCount: this.toolExecutions.length - successCount,
                byTool,
            },
            ...(this.cacheMetrics ? { cache: this.cacheMetrics } : {}),
            ...(this.turnMetricsSummary ? { turnMetrics: this.turnMetricsSummary } : {}),
            ...(this.analysisMode ? { analysisMode: this.analysisMode } : {}),
            ...(this.classifierSource ? { classifierSource: this.classifierSource } : {}),
        };
    }
}
exports.AgentMetricsCollector = AgentMetricsCollector;
// =============================================================================
// Metrics Persistence
// =============================================================================
exports.METRICS_DIR = (0, runtimePaths_1.backendLogPath)('metrics');
const METRICS_RETENTION_DAYS = 7;
/** Write session metrics to disk. */
function persistSessionMetrics(metrics) {
    try {
        if (!fs.existsSync(exports.METRICS_DIR)) {
            fs.mkdirSync(exports.METRICS_DIR, { recursive: true });
        }
        const fileName = `session_${metrics.sessionId}_metrics.json`;
        const filePath = path.join(exports.METRICS_DIR, fileName);
        const tmpPath = filePath + '.tmp';
        fs.writeFileSync(tmpPath, JSON.stringify(metrics, null, 2));
        fs.renameSync(tmpPath, filePath);
    }
    catch (err) {
        console.warn('[AgentMetrics] Failed to persist metrics:', err.message);
    }
}
/** Clean up old metrics files (called at backend startup). */
function cleanupOldMetrics() {
    try {
        if (!fs.existsSync(exports.METRICS_DIR))
            return;
        const cutoff = Date.now() - METRICS_RETENTION_DAYS * 24 * 60 * 60 * 1000;
        const files = fs.readdirSync(exports.METRICS_DIR);
        for (const file of files) {
            if (!file.endsWith('_metrics.json'))
                continue;
            const filePath = path.join(exports.METRICS_DIR, file);
            const stat = fs.statSync(filePath);
            if (stat.mtimeMs < cutoff) {
                fs.unlinkSync(filePath);
            }
        }
    }
    catch (err) {
        console.warn('[AgentMetrics] Failed to cleanup old metrics:', err.message);
    }
}
// =============================================================================
// Helpers
// =============================================================================
function safeStringifyLength(value) {
    try {
        return JSON.stringify(value).length;
    }
    catch {
        return 0;
    }
}
//# sourceMappingURL=agentMetrics.js.map