@pai/openai-codex
Run Responses language models with an explicitly authorized ChatGPT subscription credential.
@pai/openai-codex is an experimental, server-only Vercel AI SDK provider for
local or personal PAI agents. It uses a ChatGPT subscription credential rather
than an OpenAI Platform API key.
The package delegates the Responses protocol to @ai-sdk/openai. PAI owns the
OAuth credential lifecycle, exact Codex endpoint, account pinning, and the
small set of stateless request invariants the subscription endpoint requires.
The backend requires streamed Responses, so both generateText and
streamText use the upstream stream parser. Generate calls collect the
normalized AI SDK stream into a standard generate result.
The optional raw response.body is unavailable for generated results because
the HTTP body is an SSE sequence rather than one JSON document. Normalized
content, headers, response metadata, provider metadata, and usage are retained.
Install And Authenticate
pnpm add @pai/openai-codex ai zod
pnpm exec pai-openai-codex loginUse device authorization on a headless machine:
pnpm exec pai-openai-codex login --deviceThe default credential is plaintext at
.pai/openai-codex/auth.json in the current user's home directory, protected
with owner-only permissions on Unix. Treat it as a password. Windows mode bits
are best effort; inject a keychain-backed store when stronger at-rest
protection is required.
Create A Provider
Authentication and model creation are deliberately explicit:
import {
createOpenAICodex,
createOpenAICodexAuth,
} from "@pai/openai-codex";
import { defineAgent } from "@pai/core";
const auth = createOpenAICodexAuth();
const openaiCodex = createOpenAICodex({ auth });
export const agent = defineAgent({
name: "coding-agent",
model: ({ threadId }) => ({
model: openaiCodex("gpt-5.6-sol"),
providerOptions: {
openai: { promptCacheKey: threadId },
},
}),
instructions: "Help with the user's coding task.",
});Provider and model construction perform no filesystem, browser, listener, or
network work. The first model request pins the ChatGPT account. If another
login replaces the credential with a different account, existing providers
fail with account_changed; construct a new provider before continuing.
Model Discovery
listOpenAICodexModels reads the models the signed-in ChatGPT account may use
through Codex, so a host can offer real choices instead of free-text entry:
import { listOpenAICodexModels } from "@pai/openai-codex";
const models = await listOpenAICodexModels(auth);Each entry carries its id, the displayName ChatGPT shows, a description,
and a contextLength. Only models the backend marks as listable are returned:
the rest are internal entries such as auto-review helpers, which an account can
name but which are not choices to put in front of a person.
The catalogue endpoint reveals a model only to a client at or above the
minimal_client_version that model declares. This package sends a deliberate
pin — the newest catalogue version it was verified against, with every model
returned confirmed to run over this Responses transport. The pin moves when
that verification is repeated, not with a release, so a new model the backend
gates behind a newer client appears here only once it has been checked.
The authenticated transport reaches exactly two destinations: the Responses
call and this catalogue. Any other URL, or either one with the wrong method,
fails with unsupported_request before a credential is read.
Provider Options
Use the upstream openai namespace, matching @ai-sdk/openai metadata and
tool conventions:
import type { OpenAICodexProviderOptions } from "@pai/openai-codex";
const selection = {
model: openaiCodex("gpt-5.6-sol"),
providerOptions: {
openai: {
reasoningEffort: "high",
reasoningSummary: "auto",
} satisfies OpenAICodexProviderOptions,
},
};Prompt caching
The subscription backend reused an exact 2,816-token user-message prefix in
live conformance testing when promptCacheKey was stable. Use the PAI thread
ID rather than a random value per request. Cache reads appear in
usage.inputTokenDetails.cacheReadTokens. PAI does not populate this
provider-specific option automatically.
The provider example above uses PAI's existing model resolver boundary to
derive this key directly from threadId. Add other OpenAI provider options to
that same openai object.
The same key is also sent as the session-id header. The Codex CLI sets both
from one session identity, and the ChatGPT backend uses the header to pick the
cache-affine route, so a body key alone can land on a node that does not hold
the prefix. Keep the key visible ASCII of at most 512 characters; a key that is
not still reaches the body but is not mirrored, and the call carries a warning
saying so.
The provider deliberately rejects the Platform API's promptCacheOptions and
promptCacheRetention controls. The subscription endpoint rejected
promptCacheOptions in live conformance testing; retention policy support was
not probed. Repeated top-level instructions did not produce a cache hit;
structure reusable context as stable message prefixes when prompt caching
matters.
The provider owns these settings:
- System messages become top-level Responses
instructions. storeis alwaysfalseand encrypted reasoning is requested for stateless multi-turn continuation.forceReasoningis enabled so newly available Codex model IDs retain encrypted reasoning even before the pinned AI SDK recognizes them.conversation,previousResponseId, caller-suppliedinstructions, andsystemMessageModeare rejected.- Platform-only
promptCacheOptionsandpromptCacheRetentioncontrols are rejected;promptCacheKeyremains supported. maxOutputTokensis omitted and reported as an AI SDK unsupported-setting warning.
Normal AI SDK function tools and structured output remain supported. The package does not expose the upstream provider-native tool collection because the subscription endpoint has not been verified for every OpenAI service.
Auth API
createOpenAICodexAuth() returns one manager that owns login, status, logout,
refresh serialization, and 401 recovery for a credential slot.
const auth = createOpenAICodexAuth();
await auth.login();
const status = await auth.status();
await auth.logout();For custom storage, implement OpenAICodexCredentialStore. Its Uint8Array
value is an opaque versioned secret envelope; do not parse, log, or expose it.
Reuse one store and auth manager within the process.
const auth = createOpenAICodexAuth({
credentialStore: mySecretStore,
fetch: trustedNetworkFetch,
});The optional fetch is a privileged seam that observes access and refresh
tokens. Never provide a function controlled by an agent, plugin, or tenant.
Every request carries ChatGPT-Account-Id from the token and, when the token
declares a chatgpt_compute_residency constraint, the
x-openai-internal-codex-residency header, so a residency-bound account is
routed as its own Codex CLI would route it. Both are derived from the credential
in use and cannot be supplied by a caller. Function tools are registered with
strict: false unless the tool sets its own strictness, matching the Codex CLI,
so schemas with optional fields or from MCP servers load rather than failing the
request.
Errors
Recognizable local failures use OpenAICodexError and a safe code, including
not_logged_in, refresh_rejected, account_changed,
credentials_corrupt, and unsupported_option. Messages do not include raw
OAuth responses, tokens, codes, or request headers. Upstream model API errors
remain the standard AI SDK errors produced by @ai-sdk/openai.
Scope And Limitations
This release supports one local/personal ChatGPT account and one Node process mutating a credential file. It is not a multi-tenant broker and must not pool or proxy one operator's subscription across users. It never imports or shares the Codex CLI credential file.
Only Responses language models are supported. Required AI SDK ProviderV4
embedding and image lookups throw NoSuchModelError before reading credentials;
speech and transcription are also unsupported. HTTP is the only transport.
Model availability, subscription quotas, terms, and the non-public Codex
backend can change independently of PAI. For stable production API terms or
other OpenAI capabilities, use @ai-sdk/openai with an OpenAI Platform API key.