A community provider for Vercel AI SDK v7 that integrates OpenAI's Codex CLI (for example gpt-5.5) using your ChatGPT Plus/Pro subscription. Available model slugs follow whatever your installed Codex CLI exposes — use listModels() / provider.listModels() to discover them.
This package ships two provider modes:
-
codexExec: non-interactivecodex exec(spawn a new process per call) -
codexAppServer: persistentcodex app-serverJSON-RPC client (shared process, true delta streaming, optional stateful threads) -
Works with
generateText,streamText, andgenerateObject -
Uses ChatGPT OAuth from
codex login(tokens in~/.codex/auth.json) orOPENAI_API_KEY -
Node-only (spawns a local process); supports CI and local dev
-
Requires Node.js >= 22; published as an ESM-only package
-
v2.0.0: AI SDK v7 migration with the native LanguageModelV4 provider spec (package line 2.x)
-
v1.0.0: AI SDK v6 migration with the LanguageModelV3 interface (now on the
ai-sdk-v6tag)
| Package line | AI SDK | npm tag | Git branch | Status |
|---|---|---|---|---|
| 2.x | v7 | latest, ai-sdk-v7 |
main |
Active development |
| 1.x | v6 | ai-sdk-v6 |
ai-sdk-v6 |
Maintenance |
| 0.7.x | v5 | ai-sdk-v5 |
ai-sdk-v5 |
Maintenance / critical fixes |
0.1.0-ai-sdk-v4 |
v4 | ai-sdk-v4 |
ai-sdk-v4 |
Frozen |
- Install and authenticate Codex CLI
npm i -g @openai/codex
codex login # or set OPENAI_API_KEY- Install provider and AI SDK v7
npm i ai ai-sdk-provider-codex-cliRequirements: Node.js >= 22. This package is ESM-only (no CommonJS build); load it with
import(or dynamicimport()from CJS).
npm i ai@^6 ai-sdk-provider-codex-cli@ai-sdk-v6npm i ai@^5.0.0 ai-sdk-provider-codex-cli@ai-sdk-v5
⚠️ Codex CLI Version: Requires the current stable Codex CLI 0.144.x for full support of both provider modes (codexExecandcodexAppServer). This package pins its optional@openai/codexdependency to^0.144.0, the latest non-alpha release line validated for this release line. If you supply your own Codex CLI (global install or customcodexPath), check it withcodex --versionand upgrade if needed.npm i -g @openai/codex@latest
import { generateText } from 'ai';
import { codexExec } from 'ai-sdk-provider-codex-cli';
const model = codexExec('gpt-5.5', {
allowNpx: true,
skipGitRepoCheck: true,
approvalMode: 'on-failure',
sandboxMode: 'workspace-write',
});
const { text } = await generateText({
model,
prompt: 'Reply with a single word: hello.',
});
console.log(text);import { streamText } from 'ai';
import { createCodexAppServer } from 'ai-sdk-provider-codex-cli';
const provider = createCodexAppServer({
defaultSettings: {
minCodexVersion: '0.144.0',
autoApprove: false,
personality: 'pragmatic',
},
});
const { textStream } = await streamText({
model: provider('gpt-5.5'),
prompt: 'Write two short lines of encouragement.',
});
for await (const chunk of textStream) process.stdout.write(chunk);
await provider.close();By default, codexAppServer is stateless (new ephemeral thread per call). To continue a prior conversation across calls, start a persistent thread and then pass its threadId in providerOptions['codex-app-server'].
import { generateText } from 'ai';
import { createCodexAppServer } from 'ai-sdk-provider-codex-cli';
const provider = createCodexAppServer();
const first = await generateText({
model: provider('gpt-5.5'),
prompt: 'Start a migration checklist.',
providerOptions: {
'codex-app-server': { threadMode: 'persistent' },
},
});
const threadId = first.finalStep.providerMetadata?.['codex-app-server']?.threadId;
const second = await generateText({
model: provider('gpt-5.5'),
prompt: 'Continue from step 2.',
providerOptions: {
'codex-app-server': { threadId },
},
});
await provider.close();import { generateObject } from 'ai';
import { z } from 'zod';
import { codexExec } from 'ai-sdk-provider-codex-cli';
const schema = z.object({ name: z.string(), age: z.number().int() });
const { object } = await generateObject({
model: codexExec('gpt-5.5', { allowNpx: true, skipGitRepoCheck: true }),
schema,
prompt: 'Generate a small user profile.',
});
console.log(object);- AI SDK v7 compatible (native LanguageModelV4 provider spec)
- Dual provider architecture:
codexExec/createCodexExecforcodex execcodexAppServer/createCodexAppServerforcodex app-server
- Backward-compatible aliases:
codexCli/createCodexClimap to exec mode - Model discovery via
listModels()/provider.listModels()— available slugs follow your installed Codex CLI - Streaming and non‑streaming
- Configurable logging - Verbose mode, custom loggers, or silent operation
- Tool streaming support - Monitor autonomous tool execution in real-time
- Native JSON Schema support via
--output-schema(exec) / theoutputSchematurn parameter (app-server) - JSON object generation with Zod schemas (100-200 fewer tokens per request vs prompt engineering)
- Safe defaults for non‑interactive automation (
on-failure,workspace-write,--skip-git-repo-check) - Fallback to
npx -y @openai/codexwhen the local@openai/codexpackage can't be resolved (allowNpx) - Usage tracking from experimental JSON event format
- Image support - Local binary images in both providers; remote HTTP/HTTPS image URLs work via AI SDK download
The provider supports multimodal (image) inputs for vision-capable models:
import { generateText } from 'ai';
import { codexExec } from 'ai-sdk-provider-codex-cli';
import { readFileSync } from 'fs';
const model = codexExec('gpt-5.5', { allowNpx: true, skipGitRepoCheck: true });
const imageBuffer = readFileSync('./screenshot.png');
const { text } = await generateText({
model,
messages: [
{
role: 'user',
content: [
{ type: 'text', text: 'What do you see in this image?' },
{ type: 'file', data: imageBuffer, mediaType: 'image/png' },
],
},
],
});
console.log(text);Supported image formats:
- Base64 data URL (
data:image/png;base64,...) - Base64 string (without data URL prefix)
Buffer/Uint8Array/ArrayBuffer
Remote image URLs:
- Pass remote images as
{ type: 'file', data: new URL('https://...'), mediaType: 'image/png' }message parts in either mode - Both providers declare
supportedUrls = {}, so the AI SDK downloads the URL itself and hands the provider the bytes, which flow through the same temp-file path as local images - Raw URL shapes that bypass the AI SDK's download step are warned and skipped in exec mode; app-server mode forwards them to Codex as-is, but the SDK download route above is the supported path
Local image data is written to temporary files and passed to Codex CLI via --image (or app-server localImage). Temp files are automatically cleaned up after each request.
See examples/exec/image-support.mjs and examples/app-server/image-support.mjs for complete working examples.
The provider supports comprehensive tool streaming, enabling real-time monitoring of Codex CLI's autonomous tool execution:
import { streamText } from 'ai';
import { codexExec } from 'ai-sdk-provider-codex-cli';
const result = await streamText({
model: codexExec('gpt-5.5', { allowNpx: true, skipGitRepoCheck: true }),
prompt: 'List files and count lines in the largest one',
});
for await (const part of result.stream) {
if (part.type === 'tool-call') {
console.log('🔧 Tool:', part.toolName);
}
if (part.type === 'tool-result') {
console.log('✅ Result:', part.output);
}
}What you get:
- Tool invocation events when Codex starts executing tools (exec, patch, web_search, mcp_tool_call)
- Tool input tracking with full parameter visibility
- Tool result events with complete output payloads
providerExecuted: trueon all tool calls (Codex executes autonomously, app doesn't need to)
Current behavior:
codexExec: tool outputs are delivered in finaltool-resultevents.codexAppServer: when Codex emits tool output delta notifications, the provider surfacestool-resultparts whoseoutput.type === 'output-delta'during streaming.
See examples/exec/streaming-tool-calls.mjs, examples/exec/streaming-multiple-tools.mjs, and their app-server counterparts under examples/app-server/.
Control logging verbosity and integrate with your observability stack:
import { codexExec } from 'ai-sdk-provider-codex-cli';
// Default: warn/error only (clean production output)
const model = codexExec('gpt-5.5', {
allowNpx: true,
skipGitRepoCheck: true,
});
// Verbose mode: enable debug/info logs for troubleshooting
const verboseModel = codexExec('gpt-5.5', {
allowNpx: true,
skipGitRepoCheck: true,
verbose: true, // Shows all log levels
});
// Custom logger: integrate with Winston, Pino, Datadog, etc.
const customModel = codexExec('gpt-5.5', {
allowNpx: true,
skipGitRepoCheck: true,
verbose: true,
logger: {
debug: (msg) => myLogger.debug('Codex:', msg),
info: (msg) => myLogger.info('Codex:', msg),
warn: (msg) => myLogger.warn('Codex:', msg),
error: (msg) => myLogger.error('Codex:', msg),
},
});
// Silent: disable all logging
const silentModel = codexExec('gpt-5.5', {
allowNpx: true,
skipGitRepoCheck: true,
logger: false, // No logs at all
});Log Levels:
debug: Detailed execution traces (verbose mode only)info: General execution flow (verbose mode only)warn: Warnings and misconfigurations (always shown)error: Errors and failures (always shown)
Default Logger: Adds level tags [DEBUG], [INFO], [WARN], [ERROR] to console output. Use a custom logger or logger: false if you need different formatting.
See examples/exec/logging-*.mjs and examples/app-server/logging-*.mjs for complete examples, and docs/ai-sdk-v7/guide.md for detailed configuration.
codexExec mode: Incremental streaming is not currently available with codex exec --experimental-json.
The --experimental-json output format (introduced Sept 25, 2025) currently only emits item.completed events with full text content. Incremental streaming via item.updated or delta events is not yet implemented by OpenAI.
What this means in exec mode:
streamText()works functionally but delivers the entire response in a single chunk after generation completes- No incremental text deltas—you wait for the full response, then receive it all at once
- The AI SDK's streaming interface is supported, but actual incremental streaming is not available
codexAppServer mode: supports true incremental text deltas via item/agentMessage/delta, so streamText() emits progressively as tokens arrive.
When OpenAI adds streaming support to codex exec --experimental-json, this provider will surface those deltas in exec mode as well.
- Getting started, configuration, and troubleshooting live in
docs/:- docs/ai-sdk-v7/guide.md – full usage guide and examples
- docs/ai-sdk-v7/configuration.md – all settings and how they map to CLI flags
- docs/ai-sdk-v7/troubleshooting.md – common issues and fixes
- docs/ai-sdk-v7/limitations.md – known constraints and behavior differences
- docs/ai-sdk-v7/migration-v6-to-v7.md – migrating from the 1.x (AI SDK v6) package line
- See examples/ for runnable scripts covering core usage, streaming, permissions/sandboxing, and object generation.
- Validation helpers:
npm run validate:docschecks markdown links and example command pathsnpm run validate:examples:app-serverruns all app-server examples with intent checksnpm run validate:fullruns build/type/lint/test plus docs and app-server example validation
- Preferred: ChatGPT OAuth via
codex login(stores tokens at~/.codex/auth.json) - Alternative: export
OPENAI_API_KEYin the provider’senvsettings (forwarded to the spawned process)
allowNpx: The provider prefers the locally installed@openai/codexpackage; when it can't be resolved,allowNpx: truefalls back tonpx -y @openai/codex(otherwise acodexbinary on PATH is used)cwd: Working directory for CodexaddDirs: Extra directories Codex may read/write (repeats--add-dir)- Autonomy/sandbox:
fullAuto(equivalent to--full-auto)dangerouslyBypassApprovalsAndSandbox(bypass approvals and sandbox; dangerous)- Otherwise the provider writes
-c approval_policy=...and-c sandbox_mode=...for you; defaults toon-failureandworkspace-write
skipGitRepoCheck: on by default (passfalseto keep Codex's git-repo check for CI/non‑repo safety)color:always|never|autooutputLastMessageFile: by default the provider sets a temp path and reads it to capture final text reliably- Logging:
verbose: Enable debug/info logs (default:falsefor clean output)logger: Custom logger object orfalseto disable all logging
See docs/ai-sdk-v7/configuration.md for the full list and examples.
createCodexAppServer({ defaultSettings }) accepts app-server specific options:
connectionTimeoutMs: initialize handshake timeoutrequestTimeoutMs: default per-request JSON-RPC timeoutidleTimeoutMs: close idle app-server process after inactivityminCodexVersion: minimum supported app-server version (semver)includeRawChunks: emit raw JSON-RPC notifications asrawstream parts by default (per call, prefer the standard AI SDK v7 optioninclude: { rawChunks: true }onstreamText)serverRequests: typed handlers for server-initiated JSON-RPC requestsautoApprove: default approval response when no custom handler is provided (covers command execution, file changes, skills, and MCP tool call approvals viamcpServer/elicitation/requeston Codex >= 0.139)persistExtendedHistory: request extended thread history persistencethreadMode:stateless(default) orpersistentautomatic thread reuseresume: shorthand to resume an existing thread idonSessionCreated: receive a session object forinjectMessage()/interrupt()
Per-call app-server overrides use providerOptions['codex-app-server'] (for example threadId, threadMode, includeRawChunks, personality, approvalPolicy, sandboxPolicy, serverRequests, configOverrides). Raw chunk emission can also be requested per call with the standard AI SDK v7 option include: { rawChunks: true }.
Additional app-server helpers:
listModels(): query available models via a temporary app-server process (or useprovider.listModels()to query through an existing provider/client)tool(),createLocalMcpServer(),createSdkMcpServer(): define and expose local MCP tools
Local MCP security defaults:
createLocalMcpServer()binds to loopback hosts by default and rejects non-loopbackhostvalues unless you setallowNonLoopbackHost: true.createLocalMcpServer()generates a per-server bearer token and expectsAuthorization: Bearer <token>on direct HTTP calls. The token is available atserver.config.bearerToken.createSdkMcpServer()propagates this auth config automatically, so provider-level MCP wiring works without extra manual headers.- Without
cacheKey, SDK MCP server/tool function identity participates in persistent keying to avoid conflating closure-dependent tool behavior. - Use
createSdkMcpServer({ cacheKey })when you intentionally recreate equivalent SDK MCP definitions per call and want stable persistent model reuse.
Control reasoning effort, verbosity, and advanced Codex features at model creation time:
import { codexExec } from 'ai-sdk-provider-codex-cli';
const model = codexExec('gpt-5.5', {
allowNpx: true,
skipGitRepoCheck: true,
addDirs: ['../shared'],
// Reasoning & verbosity
reasoningEffort: 'medium', // none | minimal | low | medium | high | xhigh (xhigh on codex-max and newer models that expose it)
reasoningSummary: 'auto', // auto | detailed (Note: 'concise' and 'none' are rejected by API)
reasoningSummaryFormat: 'none', // none | experimental
modelVerbosity: 'high', // low | medium | high
// Advanced features
profile: 'production', // adds --profile production
oss: false, // adds --oss when true
webSearch: true, // maps to -c tools.web_search=true
// MCP servers (stdio + HTTP/RMCP)
rmcpClient: true, // enables HTTP-based MCP clients (features.rmcp_client=true)
mcpServers: {
local: {
transport: 'stdio',
command: 'node',
args: ['tools/mcp.js'],
env: { API_KEY: process.env.MCP_API_KEY ?? '' },
},
docs: {
transport: 'http',
url: 'https://mcp.my-org.com',
bearerTokenEnvVar: 'MCP_BEARER',
httpHeaders: { 'x-tenant': 'acme' },
},
},
// Generic overrides (maps to -c key=value)
configOverrides: {
experimental_resume: '/tmp/session.jsonl',
sandbox_workspace_write: { network_access: true },
},
});Nested override objects are flattened to dotted keys (e.g., the example above emits
-c sandbox_workspace_write.network_access=true). Arrays are serialized to JSON strings.
MCP server env/header objects flatten the same way (e.g., mcp_servers.docs.http_headers.x-tenant=acme).
Override these parameters for individual AI SDK calls using the providerOptions map. Per-call
values take precedence over constructor defaults while leaving other settings intact.
import { generateText } from 'ai';
import { codexExec } from 'ai-sdk-provider-codex-cli';
const model = codexExec('gpt-5.5', {
allowNpx: true,
reasoningEffort: 'medium',
modelVerbosity: 'medium',
});
const response = await generateText({
model,
prompt: 'Summarize the latest release notes.',
providerOptions: {
'codex-cli': {
reasoningEffort: 'high',
reasoningSummary: 'detailed',
textVerbosity: 'high', // AI SDK naming; maps to model_verbosity
rmcpClient: true,
mcpServers: {
scratch: {
transport: 'stdio',
command: 'pnpm',
args: ['mcp', 'serve'],
},
},
configOverrides: {
experimental_resume: '/tmp/resume.jsonl',
},
},
},
});Precedence: providerOptions['codex-cli'] > top-level reasoning call option > constructor CodexCliSettings > Codex CLI defaults.
The AI SDK v7 top-level reasoning option ('none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh') maps directly to Codex reasoning effort in both provider modes; provider-specific effort options (reasoningEffort for exec, effort for app-server) win when both are set, and 'provider-default' leaves your configured default untouched.
App-server per-call overrides use providerOptions['codex-app-server']:
import { createCodexAppServer } from 'ai-sdk-provider-codex-cli';
const appServerProvider = createCodexAppServer();
const response = await generateText({
model: appServerProvider('gpt-5.5'),
prompt: 'Continue this task.',
providerOptions: {
'codex-app-server': {
threadId: 'thr_existing',
personality: 'pragmatic',
approvalPolicy: 'on-request',
},
},
});- Peer dependency:
zod@^4.1.8(Zod 4 only; Zod 3 is not supported)
- Node ≥ 22, ESM-only, local process only (no Edge)
- Codex
--experimental-jsonmode emits events rather than streaming deltas; streaming typically yields a final chunk. The CLI provides the final assistant text in theitem.completedevent, which this provider reads and emits at the end. - Some AI SDK parameters are unsupported by Codex CLI (e.g., temperature/topP/penalties); the provider surfaces warnings and ignores them
- Optional fields NOT supported: All fields must be required (no
.optional()) - Format validators stripped:
.email(),.url(),.uuid()are removed (use descriptions instead) - Pattern validators stripped:
.regex()is removed (use descriptions instead)
See LIMITATIONS.md for comprehensive details and migration guidance.
This is a community provider and not an official OpenAI or Vercel product. You are responsible for complying with all applicable terms and ensuring safe usage.
MIT