/** * Search-box.ai * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai * File: packages/shared/src/budgets.ts * Description: Hard safety budgets for a research session. */ import { z } from "zod"; export const BudgetsSchema = z.object({ maxSearches: z.number().int().positive().default(12), maxScrapes: z.number().int().positive().default(16), maxToolCalls: z.number().int().positive().default(60), maxModelTurns: z.number().int().positive().default(40), deadlineMs: z.number().int().positive().default(8 * 60 * 1000) }); export type Budgets = z.infer; export const DEFAULT_BUDGETS: Budgets = BudgetsSchema.parse({}); export interface BudgetUsage { searches: number; scrapes: number; toolCalls: number; modelTurns: number; startedAt: number; } export function newBudgetUsage(): BudgetUsage { return { searches: 0, scrapes: 0, toolCalls: 0, modelTurns: 0, startedAt: Date.now() }; } /** Returns a human-readable reason when a budget is exhausted, else null. */ export function budgetExceeded(b: Budgets, u: BudgetUsage): string | null { if (u.searches >= b.maxSearches) return "search budget exhausted"; if (u.scrapes >= b.maxScrapes) return "scrape budget exhausted"; if (u.toolCalls >= b.maxToolCalls) return "tool-call budget exhausted"; if (u.modelTurns >= b.maxModelTurns) return "model-turn budget exhausted"; if (Date.now() - u.startedAt >= b.deadlineMs) return "deadline reached"; return null; }