The Codex provider previously loaded node:os asynchronously during module evaluation. A fresh process that imports the provider and immediately starts an SSE request can build headers before that promise callback runs, so the first request reports User-Agent: pi (browser) in Node/Bun.
Reproduced against parent fd6659dd with a stubbed-fetch harness: 50/50 immediate first requests reported pi (browser) in both Node and Bun. The same harness on this fix reports the OS-specific user agent 50/50 in both runtimes.
Use process.getBuiltinModule("node:os") behind the existing Node/Bun runtime guard so OS metadata is available synchronously while still avoiding top-level runtime Node builtin imports that break browser/Vite builds.
Add a shared normalizeProviderError helper in packages/ai/src/utils/error-body.ts
and route the 8 body-blind / status-only providers through it (amazon-bedrock,
azure-openai-responses, google, google-vertex, images/openrouter,
openai-codex-responses, openai-completions, openai-responses). Non-schema 4xx/5xx
responses from proxies / gateways now show the real reason carried in the response
body alongside the HTTP status, instead of "403 status code (no body)" or
"Unknown: UnknownError".
The helper probes status (statusCode, status, $metadata.httpStatusCode,
$response.statusCode) and body (body, parsed error object, $response.body) across
the Mistral, OpenAI, Google, and Bedrock SDK shapes, truncates the body at a 4000
char cap, and preserves error.message when the SDK already folded the body in
(Anthropic / Google happy path). mistral.ts and anthropic.ts are left untouched.
Provider prefixes and the OpenRouter metadata.raw append are preserved.
closes#5763
Add optional reasoning?: number to Usage as a subset of output. Populate
for Anthropic (output_tokens_details.thinking_tokens), OpenAI
Responses/Codex/Azure (output_tokens_details.reasoning_tokens), OpenAI
Completions (completion_tokens_details.reasoning_tokens), and Google
Generative AI / Vertex (thoughtsTokenCount). Bedrock Converse and Mistral
do not return a reasoning breakdown, so they stay unset.
closes#6057