// transports/middleware/with-budget.ts — Per-session budget enforcement (SPEC-740)
import { getBudgetWarning } from '../../engine/session-budget.js';
/**
 * Per-session call counter.
 * Resets on process restart. Maps sessionId -> call count.
 */
const sessionCallCounts = new Map();
/** Default per-session budget cap (tool calls). Override via PLANU_SESSION_BUDGET env var. */
const DEFAULT_SESSION_BUDGET = 200;
function getSessionBudget() {
    const env = process.env.PLANU_SESSION_BUDGET;
    if (env === undefined || env === '') {
        return DEFAULT_SESSION_BUDGET;
    }
    const parsed = Number(env);
    return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_SESSION_BUDGET;
}
/**
 * Returns a structured BUDGET_EXCEEDED response without calling the inner handler.
 */
function budgetExceededResponse(remainingMs) {
    return {
        content: [
            {
                type: 'text',
                text: JSON.stringify({
                    ok: false,
                    error: 'BUDGET_EXCEEDED',
                    code: 'budget.session',
                    remainingMs,
                }),
            },
        ],
        isError: true,
        budgetExceeded: true,
    };
}
/**
 * withBudget middleware: enforces per-session call cap.
 * Short-circuits with BUDGET_EXCEEDED when limit reached.
 */
export const withBudget = (handler) => {
    return async (input, ctx) => {
        const budget = getSessionBudget();
        const current = sessionCallCounts.get(ctx.sessionId) ?? 0;
        if (current >= budget) {
            return budgetExceededResponse(0);
        }
        // Increment counter before calling inner handler
        sessionCallCounts.set(ctx.sessionId, current + 1);
        const result = await handler(input, ctx);
        // Emit a warning if approaching limit (non-blocking, just annotate)
        const warning = getBudgetWarning(current + 1, budget);
        if (warning !== null && result.isError !== true) {
            return {
                ...result,
                content: [...result.content, { type: 'text', text: warning }],
            };
        }
        return result;
    };
};
/**
 * Exposed for testing: reset session counters.
 */
export function resetSessionCounters() {
    sessionCallCounts.clear();
}
/**
 * Exposed for testing: get call count for a session.
 */
export function getSessionCallCount(sessionId) {
    return sessionCallCounts.get(sessionId) ?? 0;
}
//# sourceMappingURL=with-budget.js.map