fix(audit): AI — L8 (single recordAiUsage), L9 (budget-off warning), L10 (sanitize notes/subjects into prompt)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 12:59:16 +02:00
parent 70bf26aea1
commit aedbcfd58d
2 changed files with 44 additions and 50 deletions

View File

@@ -31,6 +31,14 @@ export interface AiBudget {
const KEY = 'ai.budget';
// Disabled by default deliberately. Shipping an enabled default would
// silently impose a token cap on every existing port the moment they
// upgrade - a surprising behaviour change for a tenant that never opened
// the AI-budget screen. Instead we keep "off by default" and emit a loud
// warning from checkBudget() whenever an AI feature actually invokes the
// gate while the budget is disabled (see L9), so the unlimited-spend
// posture is visible in logs rather than silent. Admins opt in via
// setAiBudget({ enabled: true, ... }).
const DEFAULT_BUDGET: AiBudget = {
enabled: false,
softCapTokens: 100_000,
@@ -151,6 +159,14 @@ export async function checkBudget(args: {
const budget = await readBudget(portId);
if (!budget.enabled) {
// Budget is off - usage still gets logged, but no caps enforced.
// Reaching this branch means an AI feature is live AND spending while
// the port has no spend cap configured (L9). Warn loudly so the
// unlimited-per-tenant posture surfaces in logs and an operator can
// opt the port into a cap via setAiBudget({ enabled: true }).
logger.warn(
{ portId, hardCapTokens: budget.hardCapTokens },
'AI budget disabled - no token cap enforced for this port; AI spend is unlimited until an admin enables the budget',
);
return { ok: true, remaining: Number.POSITIVE_INFINITY, usedTokens: 0, softCap: false };
}
const used = await currentPeriodTokens(portId);