import { toRecord } from '../../../utils/record-coerce.js';
export async function budgetPhase(_call, _tool, context, record, checkpoint) {
    const start = performance.now();
    const phaseName = checkpoint === 'entry' ? 'budget-entry' : 'budget-exit';
    const budget = context.budget;
    // No budget constraints — fast path
    if (!budget) {
        return ok(start, phaseName);
    }
    const elapsedMs = performance.now() - record.startedAt;
    // ── Time budget (checked at both entry and exit) ────────────────────────
    // All budget comparisons use strict > so that a value exactly at the limit is
    // still allowed — enforcement fires only when the limit is truly exceeded.
    if (budget.maxMs !== undefined && elapsedMs > budget.maxMs) {
        return exceed(start, phaseName, 'BUDGET_EXCEEDED_MS', {
            limitMs: budget.maxMs,
            elapsedMs,
        });
    }
    // ── Token and cost budgets (only meaningful at exit after execute ran) ──
    if (checkpoint === 'exit') {
        // Token budget: the tool result may carry a `tokenCount` annotation.
        // We check it if present; if absent we skip (cannot enforce unknown usage).
        const tokenCount = getTokenCount(record);
        if (budget.maxTokens !== undefined && tokenCount !== undefined && tokenCount > budget.maxTokens) {
            return exceed(start, phaseName, 'BUDGET_EXCEEDED_TOKENS', {
                limitTokens: budget.maxTokens,
                usedTokens: tokenCount,
            });
        }
        // Cost budget: same — only enforce when the tool result carries a cost.
        const costUsd = getCostUsd(record);
        if (budget.maxCostUsd !== undefined && costUsd !== undefined && costUsd > budget.maxCostUsd) {
            return exceed(start, phaseName, 'BUDGET_EXCEEDED_COST', {
                limitCostUsd: budget.maxCostUsd,
                usedCostUsd: costUsd,
            });
        }
    }
    return ok(start, phaseName);
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function ok(start, phase) {
    return {
        phase,
        success: true,
        durationMs: performance.now() - start,
    };
}
function exceed(start, phase, reason, meta) {
    const parts = Object.entries(meta)
        .map(([k, v]) => `${k}=${v}`)
        .join(', ');
    return {
        phase,
        success: false,
        durationMs: performance.now() - start,
        error: `Budget exceeded [${reason}]: ${parts}`,
        abort: true,
        budgetExceedReason: reason,
        budgetMeta: meta,
    };
}
/**
 * Extracts a token count from the tool execution record result, if available.
 * Tools may annotate their result with `tokenCount` as a numeric field.
 */
function getTokenCount(record) {
    if (!record.result)
        return undefined;
    // Double cast needed: ToolResult has no index signature, but tool implementations
    // may annotate results with extra numeric fields like `tokenCount`.
    const raw = toRecord(record.result)['tokenCount'];
    return typeof raw === 'number' ? raw : undefined;
}
/**
 * Extracts a cost annotation from the tool execution record result, if available.
 * Tools may annotate their result with `costUsd` as a numeric field.
 */
function getCostUsd(record) {
    if (!record.result)
        return undefined;
    // Double cast needed: same reason as getTokenCount above.
    const raw = toRecord(record.result)['costUsd'];
    return typeof raw === 'number' ? raw : undefined;
}
