Demo budget 402 errors propagated through all document load routes
Previously a demo user who hit their spending limit would see a generic 500 when trying to open a document - the budget error was being caught and swallowed inside the storage layer. This PR surfaces a typed `HTTP 402` with `code: "demo_budget_exceeded"` through every document-facing route, and retunes the demo cost model so only LLM calls actually count against the budget.
The storage layer change is the key move. downloadFile and getSignedUrl in storage.ts previously caught all errors and returned null. Now isDemoBudgetError(err) re-throws before the catch swallows it, so routes can intercept it specifically. documents.ts wraps downloadFile and getSignedUrl calls in try/catch blocks with a shared sendDemoBudgetError() helper that emits HTTP 402 + code: "demo_budget_exceeded". The /docx route also drops its 302 redirect in favor of proxying raw bytes, which was necessary to catch errors at the route level rather than losing them in a redirect chain.
The downloads.ts route gets matching treatment, and the frontend hooks useFetchDocxBytes and useFetchSingleDoc now parse code from the JSON body and re-throw with the budget-specific message so the UI can show a recoverable state.
The cost model retuning (c4a955e2) is a separate concern bundled in the same PR. The LLM reserve drops from $0.75 to $0.10, and non-LLM services (Vault, Legal, Skills, Matters) all default to $0.00. LLM charges now settle from usage.cost when available, with a fallback path that calls /llm/v1/models, caches pricing for 5 minutes, and uses token pricing from the catalog if no explicit cost is returned. A new UploadTooLargeError class caps uploads at 100 MB with a typed 413 response.
The final polish commit (57ba208c) stops forwarding raw error messages to the browser. The 402 body becomes a fixed string with actionable instructions; 500 responses get opaque code fields rather than internal exception text.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?