ai-sdk-provider-codex-cli 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,9 +10,14 @@
10
10
  [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/ben-vargas/ai-sdk-provider-codex-cli/issues)
11
11
  [![Latest Release](https://img.shields.io/github/v/release/ben-vargas/ai-sdk-provider-codex-cli?display_name=tag)](https://github.com/ben-vargas/ai-sdk-provider-codex-cli/releases/latest)
12
12
 
13
- A community provider for Vercel AI SDK v6 that uses OpenAI's Codex CLI (non‑interactive `codex exec`) to talk to GPT‑5.1 / GPT‑5.2 class models (`gpt-5.1`, `gpt-5.2`, the Codex-specific `gpt-5.1-codex` / `gpt-5.2-codex`, the flagship `*-codex-max`, and the lightweight `*-codex-mini` slugs) with your ChatGPT Plus/Pro subscription. The provider spawns the Codex CLI process, parses its JSONL output, and adapts it to the AI SDK LanguageModelV3 interface. Legacy GPT-5 / GPT-5-codex slugs remain compatible for existing workflows.
13
+ A community provider for Vercel AI SDK v6 that integrates OpenAI's Codex CLI with GPT‑5.1 / GPT‑5.2 class models (`gpt-5.1`, `gpt-5.2`, the Codex-specific `gpt-5.3-codex` / `gpt-5.2-codex`, the flagship `*-codex-max`, and the lightweight `*-codex-mini` slugs) using your ChatGPT Plus/Pro subscription.
14
14
 
15
- - Works with `generateText`, `streamText`, and `generateObject` (native JSON Schema support via `--output-schema`)
15
+ This package ships two provider modes:
16
+
17
+ - `codexExec`: non-interactive `codex exec` (spawn a new process per call)
18
+ - `codexAppServer`: persistent `codex app-server` JSON-RPC client (shared process, true delta streaming, optional stateful threads)
19
+
20
+ - Works with `generateText`, `streamText`, and `generateObject`
16
21
  - Uses ChatGPT OAuth from `codex login` (tokens in `~/.codex/auth.json`) or `OPENAI_API_KEY`
17
22
  - Node-only (spawns a local process); supports CI and local dev
18
23
  - **v1.0.0**: AI SDK v6 stable migration with LanguageModelV3 interface
@@ -49,7 +54,7 @@ npm i ai ai-sdk-provider-codex-cli
49
54
  npm i ai@^5.0.0 ai-sdk-provider-codex-cli@ai-sdk-v5
50
55
  ```
51
56
 
52
- > **⚠️ Codex CLI Version**: Requires Codex CLI **>= 0.42.0** for `--experimental-json` and `--output-schema` support. **>= 0.60.0 recommended** for `gpt-5.1-codex-max` and `xhigh` reasoning effort. If you supply your own Codex CLI (global install or custom `codexPath`/`allowNpx`), check it with `codex --version` and upgrade if needed. The optional dependency `@openai/codex` in this package pulls a compatible version automatically.
57
+ > **⚠️ Codex CLI Version**: Requires Codex CLI **>= 0.105.0** for full support of both provider modes (`codexExec` and `codexAppServer`). If you supply your own Codex CLI (global install or custom `codexPath`), check it with `codex --version` and upgrade if needed. The optional dependency `@openai/codex` in this package pulls a compatible version automatically.
53
58
  >
54
59
  > ```bash
55
60
  > npm i -g @openai/codex@latest
@@ -57,13 +62,13 @@ npm i ai@^5.0.0 ai-sdk-provider-codex-cli@ai-sdk-v5
57
62
 
58
63
  ## Quick Start
59
64
 
60
- Text generation
65
+ ### Exec provider (`codexExec`) — process-per-call
61
66
 
62
67
  ```js
63
68
  import { generateText } from 'ai';
64
- import { codexCli } from 'ai-sdk-provider-codex-cli';
69
+ import { codexExec } from 'ai-sdk-provider-codex-cli';
65
70
 
66
- const model = codexCli('gpt-5.1-codex', {
71
+ const model = codexExec('gpt-5.3-codex', {
67
72
  allowNpx: true,
68
73
  skipGitRepoCheck: true,
69
74
  approvalMode: 'on-failure',
@@ -77,31 +82,67 @@ const { text } = await generateText({
77
82
  console.log(text);
78
83
  ```
79
84
 
80
- Streaming
85
+ ### App-server provider (`createCodexAppServer`) — persistent process
81
86
 
82
87
  ```js
83
88
  import { streamText } from 'ai';
84
- import { codexCli } from 'ai-sdk-provider-codex-cli';
89
+ import { createCodexAppServer } from 'ai-sdk-provider-codex-cli';
90
+
91
+ const provider = createCodexAppServer({
92
+ defaultSettings: {
93
+ minCodexVersion: '0.105.0',
94
+ autoApprove: false,
95
+ personality: 'pragmatic',
96
+ },
97
+ });
85
98
 
86
- // The provider works with both `gpt-5.1` and `gpt-5.1-codex`; use the latter for
87
- // the Codex CLI specific slug. Legacy `gpt-5` slugs still work if you need them.
88
99
  const { textStream } = await streamText({
89
- model: codexCli('gpt-5.1-codex', { allowNpx: true, skipGitRepoCheck: true }),
100
+ model: provider('gpt-5.3-codex'),
90
101
  prompt: 'Write two short lines of encouragement.',
91
102
  });
92
103
  for await (const chunk of textStream) process.stdout.write(chunk);
104
+
105
+ await provider.close();
106
+ ```
107
+
108
+ ### App-server stateful threads (optional)
109
+
110
+ By default, `codexAppServer` is stateless (new ephemeral thread per call). To continue a prior conversation, pass `threadId` in `providerOptions['codex-app-server']`.
111
+
112
+ ```js
113
+ import { generateText } from 'ai';
114
+ import { createCodexAppServer } from 'ai-sdk-provider-codex-cli';
115
+
116
+ const provider = createCodexAppServer();
117
+
118
+ const first = await generateText({
119
+ model: provider('gpt-5.3-codex'),
120
+ prompt: 'Start a migration checklist.',
121
+ });
122
+
123
+ const threadId = first.providerMetadata?.['codex-app-server']?.threadId;
124
+
125
+ const second = await generateText({
126
+ model: provider('gpt-5.3-codex'),
127
+ prompt: 'Continue from step 2.',
128
+ providerOptions: {
129
+ 'codex-app-server': { threadId },
130
+ },
131
+ });
132
+
133
+ await provider.close();
93
134
  ```
94
135
 
95
- Object generation (Zod)
136
+ ### Object generation (Zod)
96
137
 
97
138
  ```js
98
139
  import { generateObject } from 'ai';
99
140
  import { z } from 'zod';
100
- import { codexCli } from 'ai-sdk-provider-codex-cli';
141
+ import { codexExec } from 'ai-sdk-provider-codex-cli';
101
142
 
102
143
  const schema = z.object({ name: z.string(), age: z.number().int() });
103
144
  const { object } = await generateObject({
104
- model: codexCli('gpt-5.1-codex', { allowNpx: true, skipGitRepoCheck: true }),
145
+ model: codexExec('gpt-5.3-codex', { allowNpx: true, skipGitRepoCheck: true }),
105
146
  schema,
106
147
  prompt: 'Generate a small user profile.',
107
148
  });
@@ -111,6 +152,10 @@ console.log(object);
111
152
  ## Features
112
153
 
113
154
  - AI SDK v6 compatible (LanguageModelV3)
155
+ - Dual provider architecture:
156
+ - `codexExec` / `createCodexExec` for `codex exec`
157
+ - `codexAppServer` / `createCodexAppServer` for `codex app-server`
158
+ - Backward-compatible aliases: `codexCli` / `createCodexCli` map to exec mode
114
159
  - Streaming and non‑streaming
115
160
  - **Configurable logging** (v0.5.0+) - Verbose mode, custom loggers, or silent operation
116
161
  - **Tool streaming support** (v0.3.0+) - Monitor autonomous tool execution in real-time
@@ -119,7 +164,7 @@ console.log(object);
119
164
  - Safe defaults for non‑interactive automation (`on-failure`, `workspace-write`, `--skip-git-repo-check`)
120
165
  - Fallback to `npx @openai/codex` when not on PATH (`allowNpx`)
121
166
  - Usage tracking from experimental JSON event format
122
- - **Image support** - Pass images to vision-capable models via `--image` flag
167
+ - **Image support** - Local binary images in both providers, plus remote HTTP/HTTPS image URLs in app-server mode
123
168
 
124
169
  ### Image Support
125
170
 
@@ -127,10 +172,10 @@ The provider supports multimodal (image) inputs for vision-capable models:
127
172
 
128
173
  ```js
129
174
  import { generateText } from 'ai';
130
- import { codexCli } from 'ai-sdk-provider-codex-cli';
175
+ import { codexExec } from 'ai-sdk-provider-codex-cli';
131
176
  import { readFileSync } from 'fs';
132
177
 
133
- const model = codexCli('gpt-5.1-codex', { allowNpx: true, skipGitRepoCheck: true });
178
+ const model = codexExec('gpt-5.3-codex', { allowNpx: true, skipGitRepoCheck: true });
134
179
  const imageBuffer = readFileSync('./screenshot.png');
135
180
 
136
181
  const { text } = await generateText({
@@ -154,13 +199,14 @@ console.log(text);
154
199
  - Base64 string (without data URL prefix)
155
200
  - `Buffer` / `Uint8Array` / `ArrayBuffer`
156
201
 
157
- **Not supported:**
202
+ **Remote image URLs:**
158
203
 
159
- - HTTP/HTTPS URLs (images must be provided as binary data)
204
+ - `codexExec` mode: HTTP/HTTPS image URLs are not supported (provide binary/image data)
205
+ - `codexAppServer` mode: HTTP/HTTPS image URLs are supported and forwarded to app-server as remote image inputs
160
206
 
161
- Images are written to temporary files and passed to Codex CLI via the `--image` flag. Temp files are automatically cleaned up after the request completes.
207
+ 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.
162
208
 
163
- See [examples/image-support.mjs](examples/image-support.mjs) for a complete working example.
209
+ See [examples/exec/image-support.mjs](examples/exec/image-support.mjs) and [examples/app-server/image-support.mjs](examples/app-server/image-support.mjs) for complete working examples.
164
210
 
165
211
  ### Tool Streaming (v0.3.0+)
166
212
 
@@ -168,10 +214,10 @@ The provider supports comprehensive tool streaming, enabling real-time monitorin
168
214
 
169
215
  ```js
170
216
  import { streamText } from 'ai';
171
- import { codexCli } from 'ai-sdk-provider-codex-cli';
217
+ import { codexExec } from 'ai-sdk-provider-codex-cli';
172
218
 
173
219
  const result = await streamText({
174
- model: codexCli('gpt-5.1-codex', { allowNpx: true, skipGitRepoCheck: true }),
220
+ model: codexExec('gpt-5.3-codex', { allowNpx: true, skipGitRepoCheck: true }),
175
221
  prompt: 'List files and count lines in the largest one',
176
222
  });
177
223
 
@@ -192,30 +238,35 @@ for await (const part of result.fullStream) {
192
238
  - Tool result events with complete output payloads
193
239
  - `providerExecuted: true` on all tool calls (Codex executes autonomously, app doesn't need to)
194
240
 
195
- **Limitation:** Real-time output streaming (`output-delta` events) not yet available. Tool outputs delivered in final `tool-result` event. See `examples/streaming-tool-calls.mjs` and `examples/streaming-multiple-tools.mjs` for usage patterns.
241
+ **Current behavior:**
242
+
243
+ - `codexExec`: tool outputs are delivered in final `tool-result` events.
244
+ - `codexAppServer`: when Codex emits tool output delta notifications, the provider surfaces `tool-result` parts with `result.type === 'output-delta'` during streaming.
245
+
246
+ See `examples/exec/streaming-tool-calls.mjs`, `examples/exec/streaming-multiple-tools.mjs`, and their app-server counterparts under `examples/app-server/`.
196
247
 
197
248
  ### Logging Configuration (v0.5.0+)
198
249
 
199
250
  Control logging verbosity and integrate with your observability stack:
200
251
 
201
252
  ```js
202
- import { codexCli } from 'ai-sdk-provider-codex-cli';
253
+ import { codexExec } from 'ai-sdk-provider-codex-cli';
203
254
 
204
255
  // Default: warn/error only (clean production output)
205
- const model = codexCli('gpt-5.1-codex', {
256
+ const model = codexExec('gpt-5.3-codex', {
206
257
  allowNpx: true,
207
258
  skipGitRepoCheck: true,
208
259
  });
209
260
 
210
261
  // Verbose mode: enable debug/info logs for troubleshooting
211
- const verboseModel = codexCli('gpt-5.1-codex', {
262
+ const verboseModel = codexExec('gpt-5.3-codex', {
212
263
  allowNpx: true,
213
264
  skipGitRepoCheck: true,
214
265
  verbose: true, // Shows all log levels
215
266
  });
216
267
 
217
268
  // Custom logger: integrate with Winston, Pino, Datadog, etc.
218
- const customModel = codexCli('gpt-5.1-codex', {
269
+ const customModel = codexExec('gpt-5.3-codex', {
219
270
  allowNpx: true,
220
271
  skipGitRepoCheck: true,
221
272
  verbose: true,
@@ -228,7 +279,7 @@ const customModel = codexCli('gpt-5.1-codex', {
228
279
  });
229
280
 
230
281
  // Silent: disable all logging
231
- const silentModel = codexCli('gpt-5.1-codex', {
282
+ const silentModel = codexExec('gpt-5.3-codex', {
232
283
  allowNpx: true,
233
284
  skipGitRepoCheck: true,
234
285
  logger: false, // No logs at all
@@ -244,23 +295,23 @@ const silentModel = codexCli('gpt-5.1-codex', {
244
295
 
245
296
  **Default Logger:** Adds level tags `[DEBUG]`, `[INFO]`, `[WARN]`, `[ERROR]` to console output. Use a custom logger or `logger: false` if you need different formatting.
246
297
 
247
- See `examples/logging-*.mjs` for complete examples and [docs/ai-sdk-v5/guide.md](docs/ai-sdk-v5/guide.md) for detailed configuration.
298
+ See `examples/exec/logging-*.mjs` and `examples/app-server/logging-*.mjs` for complete examples, and [docs/ai-sdk-v5/guide.md](docs/ai-sdk-v5/guide.md) for detailed configuration.
248
299
 
249
300
  ### Text Streaming behavior
250
301
 
251
- **Status:** Incremental streaming not currently supported with `--experimental-json` format (expected in future Codex CLI releases)
302
+ **`codexExec` mode:** Incremental streaming is not currently available with `codex exec --experimental-json`.
252
303
 
253
304
  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.
254
305
 
255
- **What this means:**
306
+ **What this means in exec mode:**
256
307
 
257
308
  - `streamText()` works functionally but delivers the entire response in a single chunk after generation completes
258
309
  - No incremental text deltas—you wait for the full response, then receive it all at once
259
310
  - The AI SDK's streaming interface is supported, but actual incremental streaming is not available
260
311
 
261
- **Future support:** The Codex CLI commit (344d4a1d) introducing experimental JSON explicitly notes: "or other item types like `item.output_delta` when we need streaming" and states "more event types and item types to come."
312
+ **`codexAppServer` mode:** supports true incremental text deltas via `item/agentMessage/delta`, so `streamText()` emits progressively as tokens arrive.
262
313
 
263
- When OpenAI adds streaming support, this provider will be updated to handle those events and enable true incremental streaming.
314
+ When OpenAI adds streaming support to `codex exec --experimental-json`, this provider will surface those deltas in exec mode as well.
264
315
 
265
316
  ## Documentation
266
317
 
@@ -269,7 +320,12 @@ When OpenAI adds streaming support, this provider will be updated to handle thos
269
320
  - [docs/ai-sdk-v5/configuration.md](docs/ai-sdk-v5/configuration.md) – all settings and how they map to CLI flags
270
321
  - [docs/ai-sdk-v5/troubleshooting.md](docs/ai-sdk-v5/troubleshooting.md) – common issues and fixes
271
322
  - [docs/ai-sdk-v5/limitations.md](docs/ai-sdk-v5/limitations.md) – known constraints and behavior differences
323
+ - [docs/ai-sdk-v5/migration-app-server-v2.md](docs/ai-sdk-v5/migration-app-server-v2.md) – app-server v2 migration notes
272
324
  - See [examples/](examples/) for runnable scripts covering core usage, streaming, permissions/sandboxing, and object generation.
325
+ - Validation helpers:
326
+ - `npm run validate:docs` checks markdown links and example command paths
327
+ - `npm run validate:examples:app-server` runs all app-server examples with intent checks
328
+ - `npm run validate:full` runs build/type/lint/test plus docs and app-server example validation
273
329
 
274
330
  ## Authentication
275
331
 
@@ -294,14 +350,45 @@ When OpenAI adds streaming support, this provider will be updated to handle thos
294
350
 
295
351
  See [docs/ai-sdk-v5/configuration.md](docs/ai-sdk-v5/configuration.md) for the full list and examples.
296
352
 
353
+ ### App-server settings highlights
354
+
355
+ `createCodexAppServer({ defaultSettings })` accepts app-server specific options:
356
+
357
+ - `connectionTimeoutMs`: initialize handshake timeout
358
+ - `requestTimeoutMs`: default per-request JSON-RPC timeout
359
+ - `idleTimeoutMs`: close idle app-server process after inactivity
360
+ - `minCodexVersion`: minimum supported app-server version (semver)
361
+ - `includeRawChunks`: emit raw JSON-RPC notifications as `raw` stream parts by default
362
+ - `serverRequests`: typed handlers for server-initiated JSON-RPC requests
363
+ - `autoApprove`: default approval response when no custom handler is provided
364
+ - `persistExtendedHistory`: request extended thread history persistence
365
+ - `threadMode`: `stateless` (default) or `persistent` automatic thread reuse
366
+ - `resume`: shorthand to resume an existing thread id
367
+ - `onSessionCreated`: receive a session object for `injectMessage()` / `interrupt()`
368
+
369
+ Per-call app-server overrides use `providerOptions['codex-app-server']` (for example `threadId`, `threadMode`, `includeRawChunks`, `personality`, `approvalPolicy`, `sandboxPolicy`, `serverRequests`, `configOverrides`).
370
+
371
+ Additional app-server helpers:
372
+
373
+ - `listModels()`: query available models via a temporary app-server process (or use `provider.listModels()` to query through an existing provider/client)
374
+ - `tool()`, `createLocalMcpServer()`, `createSdkMcpServer()`: define and expose local MCP tools
375
+
376
+ Local MCP security defaults:
377
+
378
+ - `createLocalMcpServer()` binds to loopback hosts by default and rejects non-loopback `host` values unless you set `allowNonLoopbackHost: true`.
379
+ - `createLocalMcpServer()` generates a per-server bearer token and expects `Authorization: Bearer <token>` on direct HTTP calls. The token is available at `server.config.bearerToken`.
380
+ - `createSdkMcpServer()` propagates this auth config automatically, so provider-level MCP wiring works without extra manual headers.
381
+ - Without `cacheKey`, SDK MCP server/tool function identity participates in persistent keying to avoid conflating closure-dependent tool behavior.
382
+ - Use `createSdkMcpServer({ cacheKey })` when you intentionally recreate equivalent SDK MCP definitions per call and want stable persistent model reuse.
383
+
297
384
  ## Model Parameters & Advanced Options (v0.4.0+)
298
385
 
299
386
  Control reasoning effort, verbosity, and advanced Codex features at model creation time:
300
387
 
301
388
  ```ts
302
- import { codexCli } from 'ai-sdk-provider-codex-cli';
389
+ import { codexExec } from 'ai-sdk-provider-codex-cli';
303
390
 
304
- const model = codexCli('gpt-5.1-codex', {
391
+ const model = codexExec('gpt-5.3-codex', {
305
392
  allowNpx: true,
306
393
  skipGitRepoCheck: true,
307
394
  addDirs: ['../shared'],
@@ -353,9 +440,9 @@ values take precedence over constructor defaults while leaving other settings in
353
440
 
354
441
  ```ts
355
442
  import { generateText } from 'ai';
356
- import { codexCli } from 'ai-sdk-provider-codex-cli';
443
+ import { codexExec } from 'ai-sdk-provider-codex-cli';
357
444
 
358
- const model = codexCli('gpt-5.1-codex', {
445
+ const model = codexExec('gpt-5.3-codex', {
359
446
  allowNpx: true,
360
447
  reasoningEffort: 'medium',
361
448
  modelVerbosity: 'medium',
@@ -387,6 +474,26 @@ const response = await generateText({
387
474
 
388
475
  **Precedence:** `providerOptions['codex-cli']` > constructor `CodexCliSettings` > Codex CLI defaults.
389
476
 
477
+ App-server per-call overrides use `providerOptions['codex-app-server']`:
478
+
479
+ ```ts
480
+ import { createCodexAppServer } from 'ai-sdk-provider-codex-cli';
481
+
482
+ const appServerProvider = createCodexAppServer();
483
+
484
+ const response = await generateText({
485
+ model: appServerProvider('gpt-5.3-codex'),
486
+ prompt: 'Continue this task.',
487
+ providerOptions: {
488
+ 'codex-app-server': {
489
+ threadId: 'thr_existing',
490
+ personality: 'pragmatic',
491
+ approvalPolicy: 'on-request',
492
+ },
493
+ },
494
+ });
495
+ ```
496
+
390
497
  ## Zod Compatibility
391
498
 
392
499
  - Peer supports `zod@^3 || ^4`