fix(ai): avoid codex user-agent race

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.
This commit is contained in:
Vegard Stikbakke
2026-06-30 15:21:56 +02:00
parent dd87c02cbf
commit a3cc169d97
2 changed files with 15 additions and 11 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed OpenAI Codex user-agent construction to synchronously load Node OS metadata, avoiding a startup race that could report `pi (browser)` in Node/Bun.
## [0.80.3] - 2026-06-30
### Added
+11 -11
View File
@@ -6,20 +6,20 @@ import type {
ResponseStreamEvent,
} from "openai/resources/responses/responses.js";
// NEVER convert to top-level runtime imports - breaks browser/Vite builds
let _os: typeof NodeOs | null = null;
type ProcessWithOsBuiltinModule = typeof process & {
getBuiltinModule?: (id: "node:os") => typeof NodeOs;
};
type DynamicImport = (specifier: string) => Promise<unknown>;
const dynamicImport: DynamicImport = (specifier) => import(specifier);
const NODE_OS_SPECIFIER = "node:" + "os";
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
dynamicImport(NODE_OS_SPECIFIER).then((m) => {
_os = m as typeof NodeOs;
});
function loadNodeOs(): typeof NodeOs | null {
if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) {
return null;
}
return (process as ProcessWithOsBuiltinModule).getBuiltinModule?.("node:os") ?? null;
}
// NEVER convert to top-level runtime imports - breaks browser/Vite builds
const _os: typeof NodeOs | null = loadNodeOs();
import { clampThinkingLevel } from "../models.ts";
import { registerSessionResourceCleanup } from "../session-resources.ts";
import type {