import { costRepo, type BudgetConfig } from '../../store/cost-repo.js'; export type { CostsData, BudgetConfig, ProjectBudget } from '../../store/cost-repo.js'; export interface CostEntry { timestamp: string; project: string; trigger: string; cost_usd: number; num_turns: number | null; duration_s: number | null; backend: string; mode: string; source: string; input_tokens?: number; output_tokens?: number; provider?: string; model?: string; } interface PeriodBucket { today: number; week: number; month: number; total: number; } interface ModeBuckets { total: number; api: number; plan: number; [key: string]: number; } interface TokenBucket { input: number; output: number; } /** One calendar day in the 14-day cost series (local day). date = `YYYY-MM-DD`. */ export interface DailyCostPoint { date: string; cost: number; } export interface CostSummary { today: number; week: number; month: number; total: number; byMode: Record; byProject: Record; byTrigger: Record; bySource: Record; byBackend: Record; tokens: { today: TokenBucket; month: TokenBucket; total: TokenBucket; }; entryCount: number; /** Daily budget denominator. Resolves to the project's own limit when the summary is * project-scoped and that project has an override; otherwise the global `daily_usd`. */ dailyBudget: number; /** Monthly budget denominator, resolved on the same rule as `dailyBudget`. */ monthlyBudget: number; /** Which budget the two denominators above came from. */ budgetScope: BudgetScope; /** Today's scoped spend extrapolated linearly by the elapsed fraction of the local day. * 0 when nothing was spent today; noisy early in the day (small fraction) by construction. */ forecastToday: number; /** Per-calendar-day scoped cost for the last 14 local days, oldest→newest, last = today. * Zero-filled for days with no entries; respects the `project` filter. */ dailyCost: DailyCostPoint[]; /** Project-scoped "where it goes" trigger breakdown (byTrigger with the project filter applied). * Categories with no scoped entries are simply absent (no fabricated placeholders). */ byTriggerScoped: Record; } /** 'project' — the numbers come from budget.json `projects[]`; 'global' — from the top level. */ export type BudgetScope = 'project' | 'global'; export interface ResolvedBudget { daily_usd: number; monthly_usd: number; scope: BudgetScope; } export interface BudgetStatus { withinBudget: boolean; dailyBudget: number; dailySpent: number; dailyRemaining: number; monthlyBudget: number; monthlySpent: number; monthlyRemaining: number; byMode: Record; /** The project this status was scoped to; null for the global view. */ project: string | null; /** Whether the limits above are the project's own override or the inherited globals. */ scope: BudgetScope; } /** * Resolve which budget applies to a project. Pure — takes the already-read config. * A project with no override inherits the global pair in full (overrides are pair-only). */ export declare function pickBudget(config: BudgetConfig, projectId?: string | null): ResolvedBudget; /** * Record a cost entry after a Claude invocation. * source: 'estimate' (default, from Claude CLI) or 'gateway' (from aistatus gateway usage data) * Gateway entries may have cost_usd=0 (e.g. plan mode) but still track tokens. */ declare function recordCost({ project, trigger, cost_usd, num_turns, duration_s, backend, mode, source, input_tokens, output_tokens, provider, model }: { project?: string; trigger?: string; cost_usd?: number | null; num_turns?: number | null; duration_s?: number | null; backend?: string; mode?: string; source?: string; input_tokens?: number; output_tokens?: number; provider?: string; model?: string; }): Promise; /** * Detect project from a message string. * * @deprecated Use projectStore.resolveFromMessage() instead. * * Priority: * 1. [project:xxx] explicit tag — always wins * 2. Case-insensitive substring match against context/projects/ directory names * (longest match wins when multiple project names appear in the message) * 3. 'general' fallback */ declare function detectProject(message: string | null | undefined): string; /** * Get cost summary (global, optionally filtered by project). * `opts.now` overrides the clock (default Date.now()) — used for deterministic tests of the * time-relative fields (forecastToday / the 14-day dailyCost series). */ declare function getCostSummary(project?: string | null, opts?: { now?: number; }): Promise; /** * Check budget status. Without `project` this is the global view (all spend vs the global limits); * with one, both the spend and the limits are scoped to that project — the limits being its own * override when it has one, the inherited globals otherwise. * * Advisory only: `withinBudget` is reported, never enforced. No caller gates on it. */ declare function checkBudget(project?: string | null): Promise; /** * Update budget limits. Without `project` the global pair is updated field-by-field and every * per-project override is preserved. With `project`, BOTH limits are required (overrides are * pair-only) and only that project's entry is written — globals and sibling overrides untouched. */ declare function setBudget({ daily_usd, monthly_usd, project }: { daily_usd?: number; monthly_usd?: number; project?: string | null; }): Promise<{ daily_usd: number; monthly_usd: number; scope: BudgetScope; }>; /** * Remove one project's override so it inherits the globals again. * Returns false when the project had no override (nothing written). */ declare function clearProjectBudget(project: string): Promise; /** Every project that currently overrides the globals, sorted by id. */ declare function listProjectBudgets(): Promise<{ project: string; daily_usd: number; monthly_usd: number; }[]>; /** * Format cost summary as a readable string for Slack. */ declare function formatCostReport(project?: string | null): Promise; /** Reset the project name cache. Pass an array to pre-seed the cache (for tests). */ declare function _resetProjectCache(names?: string[] | null): void; export { costRepo, recordCost, detectProject, getCostSummary, checkBudget, setBudget, clearProjectBudget, listProjectBudgets, formatCostReport, _resetProjectCache, };