Demo budget errors propagated through document preview and download routes
Document previews were returning 404s when the real failure was a `DemoBudgetExceededError` from Case Vault. kveton traced the masking to the storage helpers swallowing the error into null, then rewired the display, DOCX, URL, and download-token routes to surface a 402 instead.
storage.ts was catching all errors from downloadFile() and getSignedUrl() and returning null on failure. That meant a demo budget error looked like a missing document to the routes above, which then responded with 404. The fix adds if (isDemoBudgetError(err)) throw err in both catch blocks, letting the budget error escape and be handled explicitly by the route.
Each affected route -- display, DOCX, URL, and the download-token endpoint -- now wraps the storage call in a try/catch that calls sendDemoBudgetError() first. That helper returns 402 with code: "demo_budget_exceeded" and a fixed user-facing message pointing the user to Account > Models. A follow-up commit hardened the message text across documents.ts and downloads.ts to use a shared constant rather than inline strings.
The DOCX route was also changed from a redirect to a byte proxy here, matching the earlier display-route change: downloadFile() bytes are buffered and sent with Content-Disposition: inline rather than redirecting to a presigned URL.
On the frontend, PDF and DOCX viewers now render a specific "demo credit limit reached" message instead of the generic failed-to-load UI. Demo usage metering was tuned in the same PR: non-LLM calls (Vault, Legal, Skills, Matters) default to $0.00, and the LLM reserve dropped from $0.75 to $0.10, with actual cost charged from usage.cost or live catalog token pricing where available.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?