ai-sdk-provider-codex-cli 0.3.0 → 0.4.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
@@ -179,6 +179,73 @@ When OpenAI adds streaming support, this provider will be updated to handle thos
179
179
 
180
180
  See [docs/ai-sdk-v5/configuration.md](docs/ai-sdk-v5/configuration.md) for the full list and examples.
181
181
 
182
+ ## Model Parameters & Advanced Options (v0.4.0+)
183
+
184
+ Control reasoning effort, verbosity, and advanced Codex features at model creation time:
185
+
186
+ ```ts
187
+ import { codexCli } from 'ai-sdk-provider-codex-cli';
188
+
189
+ const model = codexCli('gpt-5-codex', {
190
+ allowNpx: true,
191
+ skipGitRepoCheck: true,
192
+
193
+ // Reasoning & verbosity
194
+ reasoningEffort: 'medium', // minimal | low | medium | high
195
+ reasoningSummary: 'auto', // auto | detailed (Note: 'concise' and 'none' are rejected by API)
196
+ reasoningSummaryFormat: 'none', // none | experimental
197
+ modelVerbosity: 'high', // low | medium | high
198
+
199
+ // Advanced features
200
+ includePlanTool: true, // adds --include-plan-tool
201
+ profile: 'production', // adds --profile production
202
+ oss: false, // adds --oss when true
203
+ webSearch: true, // maps to -c tools.web_search=true
204
+
205
+ // Generic overrides (maps to -c key=value)
206
+ configOverrides: {
207
+ experimental_resume: '/tmp/session.jsonl',
208
+ sandbox_workspace_write: { network_access: true },
209
+ },
210
+ });
211
+ ```
212
+
213
+ Nested override objects are flattened to dotted keys (e.g., the example above emits
214
+ `-c sandbox_workspace_write.network_access=true`). Arrays are serialized to JSON strings.
215
+
216
+ ### Per-call overrides via `providerOptions` (v0.4.0+)
217
+
218
+ Override these parameters for individual AI SDK calls using the `providerOptions` map. Per-call
219
+ values take precedence over constructor defaults while leaving other settings intact.
220
+
221
+ ```ts
222
+ import { generateText } from 'ai';
223
+ import { codexCli } from 'ai-sdk-provider-codex-cli';
224
+
225
+ const model = codexCli('gpt-5-codex', {
226
+ allowNpx: true,
227
+ reasoningEffort: 'medium',
228
+ modelVerbosity: 'medium',
229
+ });
230
+
231
+ const response = await generateText({
232
+ model,
233
+ prompt: 'Summarize the latest release notes.',
234
+ providerOptions: {
235
+ 'codex-cli': {
236
+ reasoningEffort: 'high',
237
+ reasoningSummary: 'detailed',
238
+ textVerbosity: 'high', // AI SDK naming; maps to model_verbosity
239
+ configOverrides: {
240
+ experimental_resume: '/tmp/resume.jsonl',
241
+ },
242
+ },
243
+ },
244
+ });
245
+ ```
246
+
247
+ **Precedence:** `providerOptions['codex-cli']` > constructor `CodexCliSettings` > Codex CLI defaults.
248
+
182
249
  ## Zod Compatibility
183
250
 
184
251
  - Peer supports `zod@^3 || ^4`
package/dist/index.cjs CHANGED
@@ -7,8 +7,8 @@ var module$1 = require('module');
7
7
  var fs = require('fs');
8
8
  var os = require('os');
9
9
  var path = require('path');
10
- var providerUtils = require('@ai-sdk/provider-utils');
11
10
  var zod = require('zod');
11
+ var providerUtils = require('@ai-sdk/provider-utils');
12
12
 
13
13
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
14
14
  // src/codex-cli-provider.ts
@@ -41,7 +41,29 @@ var settingsSchema = zod.z.object({
41
41
  allowNpx: zod.z.boolean().optional(),
42
42
  env: zod.z.record(zod.z.string(), zod.z.string()).optional(),
43
43
  verbose: zod.z.boolean().optional(),
44
- logger: zod.z.any().optional()
44
+ logger: zod.z.any().optional(),
45
+ // NEW: Reasoning & Verbosity
46
+ reasoningEffort: zod.z.enum(["minimal", "low", "medium", "high"]).optional(),
47
+ // Note: API rejects 'concise' and 'none' despite error messages claiming they're valid
48
+ reasoningSummary: zod.z.enum(["auto", "detailed"]).optional(),
49
+ reasoningSummaryFormat: zod.z.enum(["none", "experimental"]).optional(),
50
+ modelVerbosity: zod.z.enum(["low", "medium", "high"]).optional(),
51
+ // NEW: Advanced features
52
+ includePlanTool: zod.z.boolean().optional(),
53
+ profile: zod.z.string().optional(),
54
+ oss: zod.z.boolean().optional(),
55
+ webSearch: zod.z.boolean().optional(),
56
+ // NEW: Generic overrides
57
+ configOverrides: zod.z.record(
58
+ zod.z.string(),
59
+ zod.z.union([
60
+ zod.z.string(),
61
+ zod.z.number(),
62
+ zod.z.boolean(),
63
+ zod.z.object({}).passthrough(),
64
+ zod.z.array(zod.z.any())
65
+ ])
66
+ ).optional()
45
67
  }).strict();
46
68
  function validateSettings(settings) {
47
69
  const warnings = [];
@@ -169,6 +191,22 @@ function isAuthenticationError(err) {
169
191
  }
170
192
 
171
193
  // src/codex-cli-language-model.ts
194
+ var codexCliProviderOptionsSchema = zod.z.object({
195
+ reasoningEffort: zod.z.enum(["minimal", "low", "medium", "high"]).optional(),
196
+ reasoningSummary: zod.z.enum(["auto", "detailed"]).optional(),
197
+ reasoningSummaryFormat: zod.z.enum(["none", "experimental"]).optional(),
198
+ textVerbosity: zod.z.enum(["low", "medium", "high"]).optional(),
199
+ configOverrides: zod.z.record(
200
+ zod.z.string(),
201
+ zod.z.union([
202
+ zod.z.string(),
203
+ zod.z.number(),
204
+ zod.z.boolean(),
205
+ zod.z.object({}).passthrough(),
206
+ zod.z.array(zod.z.any())
207
+ ])
208
+ ).optional()
209
+ }).strict();
172
210
  function resolveCodexPath(explicitPath, allowNpx) {
173
211
  if (explicitPath) return { cmd: "node", args: [explicitPath] };
174
212
  try {
@@ -202,6 +240,21 @@ var CodexCliLanguageModel = class {
202
240
  const warn = validateModelId(this.modelId);
203
241
  if (warn) this.logger.warn(`Codex CLI model: ${warn}`);
204
242
  }
243
+ mergeSettings(providerOptions) {
244
+ if (!providerOptions) return this.settings;
245
+ const mergedConfigOverrides = providerOptions.configOverrides || this.settings.configOverrides ? {
246
+ ...this.settings.configOverrides ?? {},
247
+ ...providerOptions.configOverrides ?? {}
248
+ } : void 0;
249
+ return {
250
+ ...this.settings,
251
+ reasoningEffort: providerOptions.reasoningEffort ?? this.settings.reasoningEffort,
252
+ reasoningSummary: providerOptions.reasoningSummary ?? this.settings.reasoningSummary,
253
+ reasoningSummaryFormat: providerOptions.reasoningSummaryFormat ?? this.settings.reasoningSummaryFormat,
254
+ modelVerbosity: providerOptions.textVerbosity ?? this.settings.modelVerbosity,
255
+ configOverrides: mergedConfigOverrides
256
+ };
257
+ }
205
258
  // Codex JSONL items use `type` for the item discriminator, but some
206
259
  // earlier fixtures (and defensive parsing) might still surface `item_type`.
207
260
  // This helper returns whichever is present.
@@ -212,28 +265,57 @@ var CodexCliLanguageModel = class {
212
265
  const current = typeof data.type === "string" ? data.type : void 0;
213
266
  return legacy ?? current;
214
267
  }
215
- buildArgs(promptText, responseFormat) {
216
- const base = resolveCodexPath(this.settings.codexPath, this.settings.allowNpx);
268
+ buildArgs(promptText, responseFormat, settings = this.settings) {
269
+ const base = resolveCodexPath(settings.codexPath, settings.allowNpx);
217
270
  const args = [...base.args, "exec", "--experimental-json"];
218
- if (this.settings.fullAuto) {
271
+ if (settings.fullAuto) {
219
272
  args.push("--full-auto");
220
- } else if (this.settings.dangerouslyBypassApprovalsAndSandbox) {
273
+ } else if (settings.dangerouslyBypassApprovalsAndSandbox) {
221
274
  args.push("--dangerously-bypass-approvals-and-sandbox");
222
275
  } else {
223
- const approval = this.settings.approvalMode ?? "on-failure";
276
+ const approval = settings.approvalMode ?? "on-failure";
224
277
  args.push("-c", `approval_policy=${approval}`);
225
- const sandbox = this.settings.sandboxMode ?? "workspace-write";
278
+ const sandbox = settings.sandboxMode ?? "workspace-write";
226
279
  args.push("-c", `sandbox_mode=${sandbox}`);
227
280
  }
228
- if (this.settings.skipGitRepoCheck !== false) {
281
+ if (settings.skipGitRepoCheck !== false) {
229
282
  args.push("--skip-git-repo-check");
230
283
  }
231
- if (this.settings.color) {
232
- args.push("--color", this.settings.color);
284
+ if (settings.reasoningEffort) {
285
+ args.push("-c", `model_reasoning_effort=${settings.reasoningEffort}`);
286
+ }
287
+ if (settings.reasoningSummary) {
288
+ args.push("-c", `model_reasoning_summary=${settings.reasoningSummary}`);
289
+ }
290
+ if (settings.reasoningSummaryFormat) {
291
+ args.push("-c", `model_reasoning_summary_format=${settings.reasoningSummaryFormat}`);
292
+ }
293
+ if (settings.modelVerbosity) {
294
+ args.push("-c", `model_verbosity=${settings.modelVerbosity}`);
295
+ }
296
+ if (settings.includePlanTool) {
297
+ args.push("--include-plan-tool");
298
+ }
299
+ if (settings.profile) {
300
+ args.push("--profile", settings.profile);
301
+ }
302
+ if (settings.oss) {
303
+ args.push("--oss");
304
+ }
305
+ if (settings.webSearch) {
306
+ args.push("-c", "tools.web_search=true");
307
+ }
308
+ if (settings.color) {
309
+ args.push("--color", settings.color);
233
310
  }
234
311
  if (this.modelId) {
235
312
  args.push("-m", this.modelId);
236
313
  }
314
+ if (settings.configOverrides) {
315
+ for (const [key, value] of Object.entries(settings.configOverrides)) {
316
+ this.addConfigOverride(args, key, value);
317
+ }
318
+ }
237
319
  let schemaPath;
238
320
  if (responseFormat?.type === "json" && responseFormat.schema) {
239
321
  const schema = typeof responseFormat.schema === "object" ? responseFormat.schema : {};
@@ -253,16 +335,55 @@ var CodexCliLanguageModel = class {
253
335
  args.push(promptText);
254
336
  const env = {
255
337
  ...process.env,
256
- ...this.settings.env || {},
338
+ ...settings.env || {},
257
339
  RUST_LOG: process.env.RUST_LOG || "error"
258
340
  };
259
- let lastMessagePath = this.settings.outputLastMessageFile;
341
+ let lastMessagePath = settings.outputLastMessageFile;
260
342
  if (!lastMessagePath) {
261
343
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "codex-cli-"));
262
344
  lastMessagePath = path.join(dir, "last-message.txt");
263
345
  }
264
346
  args.push("--output-last-message", lastMessagePath);
265
- return { cmd: base.cmd, args, env, cwd: this.settings.cwd, lastMessagePath, schemaPath };
347
+ return { cmd: base.cmd, args, env, cwd: settings.cwd, lastMessagePath, schemaPath };
348
+ }
349
+ addConfigOverride(args, key, value) {
350
+ if (this.isPlainObject(value)) {
351
+ for (const [childKey, childValue] of Object.entries(value)) {
352
+ this.addConfigOverride(
353
+ args,
354
+ `${key}.${childKey}`,
355
+ childValue
356
+ );
357
+ }
358
+ return;
359
+ }
360
+ const serialized = this.serializeConfigValue(value);
361
+ args.push("-c", `${key}=${serialized}`);
362
+ }
363
+ /**
364
+ * Serialize a config override value into a CLI-safe string.
365
+ */
366
+ serializeConfigValue(value) {
367
+ if (typeof value === "string") return value;
368
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
369
+ if (Array.isArray(value)) {
370
+ try {
371
+ return JSON.stringify(value);
372
+ } catch {
373
+ return String(value);
374
+ }
375
+ }
376
+ if (value && typeof value === "object") {
377
+ try {
378
+ return JSON.stringify(value);
379
+ } catch {
380
+ return String(value);
381
+ }
382
+ }
383
+ return String(value);
384
+ }
385
+ isPlainObject(value) {
386
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
266
387
  }
267
388
  sanitizeJsonSchema(value) {
268
389
  if (typeof value !== "object" || value === null) {
@@ -510,10 +631,17 @@ var CodexCliLanguageModel = class {
510
631
  ...this.mapWarnings(options),
511
632
  ...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
512
633
  ];
634
+ const providerOptions = await providerUtils.parseProviderOptions({
635
+ provider: this.provider,
636
+ providerOptions: options.providerOptions,
637
+ schema: codexCliProviderOptionsSchema
638
+ });
639
+ const effectiveSettings = this.mergeSettings(providerOptions);
513
640
  const responseFormat = options.responseFormat?.type === "json" ? { type: "json", schema: options.responseFormat.schema } : void 0;
514
641
  const { cmd, args, env, cwd, lastMessagePath, schemaPath } = this.buildArgs(
515
642
  promptText,
516
- responseFormat
643
+ responseFormat,
644
+ effectiveSettings
517
645
  );
518
646
  let text = "";
519
647
  const usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
@@ -635,10 +763,17 @@ var CodexCliLanguageModel = class {
635
763
  ...this.mapWarnings(options),
636
764
  ...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
637
765
  ];
766
+ const providerOptions = await providerUtils.parseProviderOptions({
767
+ provider: this.provider,
768
+ providerOptions: options.providerOptions,
769
+ schema: codexCliProviderOptionsSchema
770
+ });
771
+ const effectiveSettings = this.mergeSettings(providerOptions);
638
772
  const responseFormat = options.responseFormat?.type === "json" ? { type: "json", schema: options.responseFormat.schema } : void 0;
639
773
  const { cmd, args, env, cwd, lastMessagePath, schemaPath } = this.buildArgs(
640
774
  promptText,
641
- responseFormat
775
+ responseFormat,
776
+ effectiveSettings
642
777
  );
643
778
  const stream = new ReadableStream({
644
779
  start: (controller) => {
package/dist/index.d.cts CHANGED
@@ -6,6 +6,15 @@ interface Logger {
6
6
  }
7
7
  type ApprovalMode = 'untrusted' | 'on-failure' | 'on-request' | 'never';
8
8
  type SandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
9
+ type ReasoningEffort = 'minimal' | 'low' | 'medium' | 'high';
10
+ /**
11
+ * Reasoning summary detail level.
12
+ * Note: The API error messages claim 'concise' and 'none' are valid, but they are
13
+ * actually rejected with 400 errors. Only 'auto' and 'detailed' work in practice.
14
+ */
15
+ type ReasoningSummary = 'auto' | 'detailed';
16
+ type ReasoningSummaryFormat = 'none' | 'experimental';
17
+ type ModelVerbosity = 'low' | 'medium' | 'high';
9
18
  interface CodexCliSettings {
10
19
  codexPath?: string;
11
20
  cwd?: string;
@@ -20,10 +29,116 @@ interface CodexCliSettings {
20
29
  env?: Record<string, string>;
21
30
  verbose?: boolean;
22
31
  logger?: Logger | false;
32
+ /**
33
+ * Controls reasoning effort for reasoning-capable models (o3, o4-mini, gpt-5, gpt-5-codex).
34
+ * Higher effort produces more thorough reasoning at the cost of latency.
35
+ *
36
+ * Maps to: `-c model_reasoning_effort=<value>`
37
+ * @see https://platform.openai.com/docs/guides/reasoning
38
+ */
39
+ reasoningEffort?: ReasoningEffort;
40
+ /**
41
+ * Controls reasoning summary detail level.
42
+ *
43
+ * Valid values: 'auto' | 'detailed'
44
+ * Note: Despite API error messages claiming 'concise' and 'none' are valid,
45
+ * they are rejected with 400 errors in practice.
46
+ *
47
+ * Maps to: `-c model_reasoning_summary=<value>`
48
+ * @see https://platform.openai.com/docs/guides/reasoning#reasoning-summaries
49
+ */
50
+ reasoningSummary?: ReasoningSummary;
51
+ /**
52
+ * Controls reasoning summary format (experimental).
53
+ *
54
+ * Maps to: `-c model_reasoning_summary_format=<value>`
55
+ */
56
+ reasoningSummaryFormat?: ReasoningSummaryFormat;
57
+ /**
58
+ * Controls output length/detail for GPT-5 family models.
59
+ * Only applies to models using the Responses API.
60
+ *
61
+ * Maps to: `-c model_verbosity=<value>`
62
+ */
63
+ modelVerbosity?: ModelVerbosity;
64
+ /**
65
+ * Include experimental plan tool that the model can use to update its current plan.
66
+ *
67
+ * Maps to: `--include-plan-tool`
68
+ */
69
+ includePlanTool?: boolean;
70
+ /**
71
+ * Configuration profile from config.toml to specify default options.
72
+ *
73
+ * Maps to: `--profile <name>`
74
+ */
75
+ profile?: string;
76
+ /**
77
+ * Use OSS provider (experimental).
78
+ *
79
+ * Maps to: `--oss`
80
+ */
81
+ oss?: boolean;
82
+ /**
83
+ * Enable web search tool for the model.
84
+ *
85
+ * Maps to: `-c tools.web_search=true`
86
+ */
87
+ webSearch?: boolean;
88
+ /**
89
+ * Generic Codex CLI config overrides. Allows setting any config value
90
+ * without updating the provider.
91
+ *
92
+ * Each entry maps to: `-c <key>=<value>`
93
+ *
94
+ * Examples:
95
+ * - `{ experimental_resume: '/tmp/session.jsonl' }`
96
+ * - `{ 'model_providers.custom.base_url': 'http://localhost:8000' }`
97
+ * - `{ 'sandbox_workspace_write': { network_access: true } }`
98
+ *
99
+ * Values are serialized:
100
+ * - string → raw string
101
+ * - number/boolean → String(value)
102
+ * - plain objects → flattened recursively to dotted keys
103
+ * - arrays → JSON.stringify(value)
104
+ * - other objects (Date, RegExp, Map, etc.) → JSON.stringify(value)
105
+ */
106
+ configOverrides?: Record<string, string | number | boolean | object>;
23
107
  }
24
108
  interface CodexCliProviderSettings {
25
109
  defaultSettings?: CodexCliSettings;
26
110
  }
111
+ /**
112
+ * Per-call overrides supplied through AI SDK providerOptions.
113
+ * These values take precedence over constructor-level CodexCliSettings.
114
+ */
115
+ interface CodexCliProviderOptions {
116
+ /**
117
+ * Per-call override for reasoning depth.
118
+ * Maps to `model_reasoning_effort`.
119
+ */
120
+ reasoningEffort?: ReasoningEffort;
121
+ /**
122
+ * Per-call override for reasoning summary detail level.
123
+ * Maps to `model_reasoning_summary`.
124
+ */
125
+ reasoningSummary?: ReasoningSummary;
126
+ /**
127
+ * Per-call override for reasoning summary format.
128
+ * Maps to `model_reasoning_summary_format`.
129
+ */
130
+ reasoningSummaryFormat?: ReasoningSummaryFormat;
131
+ /**
132
+ * AI SDK naming for per-call verbosity overrides.
133
+ * Maps to Codex `model_verbosity`.
134
+ */
135
+ textVerbosity?: ModelVerbosity;
136
+ /**
137
+ * Per-call Codex CLI config overrides. These are merged with
138
+ * constructor-level overrides with per-call values taking precedence.
139
+ */
140
+ configOverrides?: Record<string, string | number | boolean | object>;
141
+ }
27
142
 
28
143
  interface CodexCliProvider extends ProviderV2 {
29
144
  (modelId: string, settings?: CodexCliSettings): LanguageModelV2;
@@ -51,8 +166,15 @@ declare class CodexCliLanguageModel implements LanguageModelV2 {
51
166
  private logger;
52
167
  private sessionId?;
53
168
  constructor(options: CodexLanguageModelOptions);
169
+ private mergeSettings;
54
170
  private getItemType;
55
171
  private buildArgs;
172
+ private addConfigOverride;
173
+ /**
174
+ * Serialize a config override value into a CLI-safe string.
175
+ */
176
+ private serializeConfigValue;
177
+ private isPlainObject;
56
178
  private sanitizeJsonSchema;
57
179
  private mapWarnings;
58
180
  private parseExperimentalJsonEvent;
@@ -70,4 +192,4 @@ declare class CodexCliLanguageModel implements LanguageModelV2 {
70
192
 
71
193
  declare function isAuthenticationError(err: unknown): boolean;
72
194
 
73
- export { CodexCliLanguageModel, type CodexCliProvider, type CodexCliProviderSettings, type CodexCliSettings, type Logger, codexCli, createCodexCli, isAuthenticationError };
195
+ export { CodexCliLanguageModel, type CodexCliProvider, type CodexCliProviderOptions, type CodexCliProviderSettings, type CodexCliSettings, type Logger, type ModelVerbosity, type ReasoningEffort, type ReasoningSummary, type ReasoningSummaryFormat, codexCli, createCodexCli, isAuthenticationError };
package/dist/index.d.ts CHANGED
@@ -6,6 +6,15 @@ interface Logger {
6
6
  }
7
7
  type ApprovalMode = 'untrusted' | 'on-failure' | 'on-request' | 'never';
8
8
  type SandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
9
+ type ReasoningEffort = 'minimal' | 'low' | 'medium' | 'high';
10
+ /**
11
+ * Reasoning summary detail level.
12
+ * Note: The API error messages claim 'concise' and 'none' are valid, but they are
13
+ * actually rejected with 400 errors. Only 'auto' and 'detailed' work in practice.
14
+ */
15
+ type ReasoningSummary = 'auto' | 'detailed';
16
+ type ReasoningSummaryFormat = 'none' | 'experimental';
17
+ type ModelVerbosity = 'low' | 'medium' | 'high';
9
18
  interface CodexCliSettings {
10
19
  codexPath?: string;
11
20
  cwd?: string;
@@ -20,10 +29,116 @@ interface CodexCliSettings {
20
29
  env?: Record<string, string>;
21
30
  verbose?: boolean;
22
31
  logger?: Logger | false;
32
+ /**
33
+ * Controls reasoning effort for reasoning-capable models (o3, o4-mini, gpt-5, gpt-5-codex).
34
+ * Higher effort produces more thorough reasoning at the cost of latency.
35
+ *
36
+ * Maps to: `-c model_reasoning_effort=<value>`
37
+ * @see https://platform.openai.com/docs/guides/reasoning
38
+ */
39
+ reasoningEffort?: ReasoningEffort;
40
+ /**
41
+ * Controls reasoning summary detail level.
42
+ *
43
+ * Valid values: 'auto' | 'detailed'
44
+ * Note: Despite API error messages claiming 'concise' and 'none' are valid,
45
+ * they are rejected with 400 errors in practice.
46
+ *
47
+ * Maps to: `-c model_reasoning_summary=<value>`
48
+ * @see https://platform.openai.com/docs/guides/reasoning#reasoning-summaries
49
+ */
50
+ reasoningSummary?: ReasoningSummary;
51
+ /**
52
+ * Controls reasoning summary format (experimental).
53
+ *
54
+ * Maps to: `-c model_reasoning_summary_format=<value>`
55
+ */
56
+ reasoningSummaryFormat?: ReasoningSummaryFormat;
57
+ /**
58
+ * Controls output length/detail for GPT-5 family models.
59
+ * Only applies to models using the Responses API.
60
+ *
61
+ * Maps to: `-c model_verbosity=<value>`
62
+ */
63
+ modelVerbosity?: ModelVerbosity;
64
+ /**
65
+ * Include experimental plan tool that the model can use to update its current plan.
66
+ *
67
+ * Maps to: `--include-plan-tool`
68
+ */
69
+ includePlanTool?: boolean;
70
+ /**
71
+ * Configuration profile from config.toml to specify default options.
72
+ *
73
+ * Maps to: `--profile <name>`
74
+ */
75
+ profile?: string;
76
+ /**
77
+ * Use OSS provider (experimental).
78
+ *
79
+ * Maps to: `--oss`
80
+ */
81
+ oss?: boolean;
82
+ /**
83
+ * Enable web search tool for the model.
84
+ *
85
+ * Maps to: `-c tools.web_search=true`
86
+ */
87
+ webSearch?: boolean;
88
+ /**
89
+ * Generic Codex CLI config overrides. Allows setting any config value
90
+ * without updating the provider.
91
+ *
92
+ * Each entry maps to: `-c <key>=<value>`
93
+ *
94
+ * Examples:
95
+ * - `{ experimental_resume: '/tmp/session.jsonl' }`
96
+ * - `{ 'model_providers.custom.base_url': 'http://localhost:8000' }`
97
+ * - `{ 'sandbox_workspace_write': { network_access: true } }`
98
+ *
99
+ * Values are serialized:
100
+ * - string → raw string
101
+ * - number/boolean → String(value)
102
+ * - plain objects → flattened recursively to dotted keys
103
+ * - arrays → JSON.stringify(value)
104
+ * - other objects (Date, RegExp, Map, etc.) → JSON.stringify(value)
105
+ */
106
+ configOverrides?: Record<string, string | number | boolean | object>;
23
107
  }
24
108
  interface CodexCliProviderSettings {
25
109
  defaultSettings?: CodexCliSettings;
26
110
  }
111
+ /**
112
+ * Per-call overrides supplied through AI SDK providerOptions.
113
+ * These values take precedence over constructor-level CodexCliSettings.
114
+ */
115
+ interface CodexCliProviderOptions {
116
+ /**
117
+ * Per-call override for reasoning depth.
118
+ * Maps to `model_reasoning_effort`.
119
+ */
120
+ reasoningEffort?: ReasoningEffort;
121
+ /**
122
+ * Per-call override for reasoning summary detail level.
123
+ * Maps to `model_reasoning_summary`.
124
+ */
125
+ reasoningSummary?: ReasoningSummary;
126
+ /**
127
+ * Per-call override for reasoning summary format.
128
+ * Maps to `model_reasoning_summary_format`.
129
+ */
130
+ reasoningSummaryFormat?: ReasoningSummaryFormat;
131
+ /**
132
+ * AI SDK naming for per-call verbosity overrides.
133
+ * Maps to Codex `model_verbosity`.
134
+ */
135
+ textVerbosity?: ModelVerbosity;
136
+ /**
137
+ * Per-call Codex CLI config overrides. These are merged with
138
+ * constructor-level overrides with per-call values taking precedence.
139
+ */
140
+ configOverrides?: Record<string, string | number | boolean | object>;
141
+ }
27
142
 
28
143
  interface CodexCliProvider extends ProviderV2 {
29
144
  (modelId: string, settings?: CodexCliSettings): LanguageModelV2;
@@ -51,8 +166,15 @@ declare class CodexCliLanguageModel implements LanguageModelV2 {
51
166
  private logger;
52
167
  private sessionId?;
53
168
  constructor(options: CodexLanguageModelOptions);
169
+ private mergeSettings;
54
170
  private getItemType;
55
171
  private buildArgs;
172
+ private addConfigOverride;
173
+ /**
174
+ * Serialize a config override value into a CLI-safe string.
175
+ */
176
+ private serializeConfigValue;
177
+ private isPlainObject;
56
178
  private sanitizeJsonSchema;
57
179
  private mapWarnings;
58
180
  private parseExperimentalJsonEvent;
@@ -70,4 +192,4 @@ declare class CodexCliLanguageModel implements LanguageModelV2 {
70
192
 
71
193
  declare function isAuthenticationError(err: unknown): boolean;
72
194
 
73
- export { CodexCliLanguageModel, type CodexCliProvider, type CodexCliProviderSettings, type CodexCliSettings, type Logger, codexCli, createCodexCli, isAuthenticationError };
195
+ export { CodexCliLanguageModel, type CodexCliProvider, type CodexCliProviderOptions, type CodexCliProviderSettings, type CodexCliSettings, type Logger, type ModelVerbosity, type ReasoningEffort, type ReasoningSummary, type ReasoningSummaryFormat, codexCli, createCodexCli, isAuthenticationError };
package/dist/index.js CHANGED
@@ -5,8 +5,8 @@ import { createRequire } from 'module';
5
5
  import { mkdtempSync, writeFileSync, rmSync, readFileSync } from 'fs';
6
6
  import { tmpdir } from 'os';
7
7
  import { join, dirname } from 'path';
8
- import { generateId } from '@ai-sdk/provider-utils';
9
8
  import { z } from 'zod';
9
+ import { parseProviderOptions, generateId } from '@ai-sdk/provider-utils';
10
10
 
11
11
  // src/codex-cli-provider.ts
12
12
 
@@ -38,7 +38,29 @@ var settingsSchema = z.object({
38
38
  allowNpx: z.boolean().optional(),
39
39
  env: z.record(z.string(), z.string()).optional(),
40
40
  verbose: z.boolean().optional(),
41
- logger: z.any().optional()
41
+ logger: z.any().optional(),
42
+ // NEW: Reasoning & Verbosity
43
+ reasoningEffort: z.enum(["minimal", "low", "medium", "high"]).optional(),
44
+ // Note: API rejects 'concise' and 'none' despite error messages claiming they're valid
45
+ reasoningSummary: z.enum(["auto", "detailed"]).optional(),
46
+ reasoningSummaryFormat: z.enum(["none", "experimental"]).optional(),
47
+ modelVerbosity: z.enum(["low", "medium", "high"]).optional(),
48
+ // NEW: Advanced features
49
+ includePlanTool: z.boolean().optional(),
50
+ profile: z.string().optional(),
51
+ oss: z.boolean().optional(),
52
+ webSearch: z.boolean().optional(),
53
+ // NEW: Generic overrides
54
+ configOverrides: z.record(
55
+ z.string(),
56
+ z.union([
57
+ z.string(),
58
+ z.number(),
59
+ z.boolean(),
60
+ z.object({}).passthrough(),
61
+ z.array(z.any())
62
+ ])
63
+ ).optional()
42
64
  }).strict();
43
65
  function validateSettings(settings) {
44
66
  const warnings = [];
@@ -166,6 +188,22 @@ function isAuthenticationError(err) {
166
188
  }
167
189
 
168
190
  // src/codex-cli-language-model.ts
191
+ var codexCliProviderOptionsSchema = z.object({
192
+ reasoningEffort: z.enum(["minimal", "low", "medium", "high"]).optional(),
193
+ reasoningSummary: z.enum(["auto", "detailed"]).optional(),
194
+ reasoningSummaryFormat: z.enum(["none", "experimental"]).optional(),
195
+ textVerbosity: z.enum(["low", "medium", "high"]).optional(),
196
+ configOverrides: z.record(
197
+ z.string(),
198
+ z.union([
199
+ z.string(),
200
+ z.number(),
201
+ z.boolean(),
202
+ z.object({}).passthrough(),
203
+ z.array(z.any())
204
+ ])
205
+ ).optional()
206
+ }).strict();
169
207
  function resolveCodexPath(explicitPath, allowNpx) {
170
208
  if (explicitPath) return { cmd: "node", args: [explicitPath] };
171
209
  try {
@@ -199,6 +237,21 @@ var CodexCliLanguageModel = class {
199
237
  const warn = validateModelId(this.modelId);
200
238
  if (warn) this.logger.warn(`Codex CLI model: ${warn}`);
201
239
  }
240
+ mergeSettings(providerOptions) {
241
+ if (!providerOptions) return this.settings;
242
+ const mergedConfigOverrides = providerOptions.configOverrides || this.settings.configOverrides ? {
243
+ ...this.settings.configOverrides ?? {},
244
+ ...providerOptions.configOverrides ?? {}
245
+ } : void 0;
246
+ return {
247
+ ...this.settings,
248
+ reasoningEffort: providerOptions.reasoningEffort ?? this.settings.reasoningEffort,
249
+ reasoningSummary: providerOptions.reasoningSummary ?? this.settings.reasoningSummary,
250
+ reasoningSummaryFormat: providerOptions.reasoningSummaryFormat ?? this.settings.reasoningSummaryFormat,
251
+ modelVerbosity: providerOptions.textVerbosity ?? this.settings.modelVerbosity,
252
+ configOverrides: mergedConfigOverrides
253
+ };
254
+ }
202
255
  // Codex JSONL items use `type` for the item discriminator, but some
203
256
  // earlier fixtures (and defensive parsing) might still surface `item_type`.
204
257
  // This helper returns whichever is present.
@@ -209,28 +262,57 @@ var CodexCliLanguageModel = class {
209
262
  const current = typeof data.type === "string" ? data.type : void 0;
210
263
  return legacy ?? current;
211
264
  }
212
- buildArgs(promptText, responseFormat) {
213
- const base = resolveCodexPath(this.settings.codexPath, this.settings.allowNpx);
265
+ buildArgs(promptText, responseFormat, settings = this.settings) {
266
+ const base = resolveCodexPath(settings.codexPath, settings.allowNpx);
214
267
  const args = [...base.args, "exec", "--experimental-json"];
215
- if (this.settings.fullAuto) {
268
+ if (settings.fullAuto) {
216
269
  args.push("--full-auto");
217
- } else if (this.settings.dangerouslyBypassApprovalsAndSandbox) {
270
+ } else if (settings.dangerouslyBypassApprovalsAndSandbox) {
218
271
  args.push("--dangerously-bypass-approvals-and-sandbox");
219
272
  } else {
220
- const approval = this.settings.approvalMode ?? "on-failure";
273
+ const approval = settings.approvalMode ?? "on-failure";
221
274
  args.push("-c", `approval_policy=${approval}`);
222
- const sandbox = this.settings.sandboxMode ?? "workspace-write";
275
+ const sandbox = settings.sandboxMode ?? "workspace-write";
223
276
  args.push("-c", `sandbox_mode=${sandbox}`);
224
277
  }
225
- if (this.settings.skipGitRepoCheck !== false) {
278
+ if (settings.skipGitRepoCheck !== false) {
226
279
  args.push("--skip-git-repo-check");
227
280
  }
228
- if (this.settings.color) {
229
- args.push("--color", this.settings.color);
281
+ if (settings.reasoningEffort) {
282
+ args.push("-c", `model_reasoning_effort=${settings.reasoningEffort}`);
283
+ }
284
+ if (settings.reasoningSummary) {
285
+ args.push("-c", `model_reasoning_summary=${settings.reasoningSummary}`);
286
+ }
287
+ if (settings.reasoningSummaryFormat) {
288
+ args.push("-c", `model_reasoning_summary_format=${settings.reasoningSummaryFormat}`);
289
+ }
290
+ if (settings.modelVerbosity) {
291
+ args.push("-c", `model_verbosity=${settings.modelVerbosity}`);
292
+ }
293
+ if (settings.includePlanTool) {
294
+ args.push("--include-plan-tool");
295
+ }
296
+ if (settings.profile) {
297
+ args.push("--profile", settings.profile);
298
+ }
299
+ if (settings.oss) {
300
+ args.push("--oss");
301
+ }
302
+ if (settings.webSearch) {
303
+ args.push("-c", "tools.web_search=true");
304
+ }
305
+ if (settings.color) {
306
+ args.push("--color", settings.color);
230
307
  }
231
308
  if (this.modelId) {
232
309
  args.push("-m", this.modelId);
233
310
  }
311
+ if (settings.configOverrides) {
312
+ for (const [key, value] of Object.entries(settings.configOverrides)) {
313
+ this.addConfigOverride(args, key, value);
314
+ }
315
+ }
234
316
  let schemaPath;
235
317
  if (responseFormat?.type === "json" && responseFormat.schema) {
236
318
  const schema = typeof responseFormat.schema === "object" ? responseFormat.schema : {};
@@ -250,16 +332,55 @@ var CodexCliLanguageModel = class {
250
332
  args.push(promptText);
251
333
  const env = {
252
334
  ...process.env,
253
- ...this.settings.env || {},
335
+ ...settings.env || {},
254
336
  RUST_LOG: process.env.RUST_LOG || "error"
255
337
  };
256
- let lastMessagePath = this.settings.outputLastMessageFile;
338
+ let lastMessagePath = settings.outputLastMessageFile;
257
339
  if (!lastMessagePath) {
258
340
  const dir = mkdtempSync(join(tmpdir(), "codex-cli-"));
259
341
  lastMessagePath = join(dir, "last-message.txt");
260
342
  }
261
343
  args.push("--output-last-message", lastMessagePath);
262
- return { cmd: base.cmd, args, env, cwd: this.settings.cwd, lastMessagePath, schemaPath };
344
+ return { cmd: base.cmd, args, env, cwd: settings.cwd, lastMessagePath, schemaPath };
345
+ }
346
+ addConfigOverride(args, key, value) {
347
+ if (this.isPlainObject(value)) {
348
+ for (const [childKey, childValue] of Object.entries(value)) {
349
+ this.addConfigOverride(
350
+ args,
351
+ `${key}.${childKey}`,
352
+ childValue
353
+ );
354
+ }
355
+ return;
356
+ }
357
+ const serialized = this.serializeConfigValue(value);
358
+ args.push("-c", `${key}=${serialized}`);
359
+ }
360
+ /**
361
+ * Serialize a config override value into a CLI-safe string.
362
+ */
363
+ serializeConfigValue(value) {
364
+ if (typeof value === "string") return value;
365
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
366
+ if (Array.isArray(value)) {
367
+ try {
368
+ return JSON.stringify(value);
369
+ } catch {
370
+ return String(value);
371
+ }
372
+ }
373
+ if (value && typeof value === "object") {
374
+ try {
375
+ return JSON.stringify(value);
376
+ } catch {
377
+ return String(value);
378
+ }
379
+ }
380
+ return String(value);
381
+ }
382
+ isPlainObject(value) {
383
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
263
384
  }
264
385
  sanitizeJsonSchema(value) {
265
386
  if (typeof value !== "object" || value === null) {
@@ -507,10 +628,17 @@ var CodexCliLanguageModel = class {
507
628
  ...this.mapWarnings(options),
508
629
  ...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
509
630
  ];
631
+ const providerOptions = await parseProviderOptions({
632
+ provider: this.provider,
633
+ providerOptions: options.providerOptions,
634
+ schema: codexCliProviderOptionsSchema
635
+ });
636
+ const effectiveSettings = this.mergeSettings(providerOptions);
510
637
  const responseFormat = options.responseFormat?.type === "json" ? { type: "json", schema: options.responseFormat.schema } : void 0;
511
638
  const { cmd, args, env, cwd, lastMessagePath, schemaPath } = this.buildArgs(
512
639
  promptText,
513
- responseFormat
640
+ responseFormat,
641
+ effectiveSettings
514
642
  );
515
643
  let text = "";
516
644
  const usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
@@ -632,10 +760,17 @@ var CodexCliLanguageModel = class {
632
760
  ...this.mapWarnings(options),
633
761
  ...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
634
762
  ];
763
+ const providerOptions = await parseProviderOptions({
764
+ provider: this.provider,
765
+ providerOptions: options.providerOptions,
766
+ schema: codexCliProviderOptionsSchema
767
+ });
768
+ const effectiveSettings = this.mergeSettings(providerOptions);
635
769
  const responseFormat = options.responseFormat?.type === "json" ? { type: "json", schema: options.responseFormat.schema } : void 0;
636
770
  const { cmd, args, env, cwd, lastMessagePath, schemaPath } = this.buildArgs(
637
771
  promptText,
638
- responseFormat
772
+ responseFormat,
773
+ effectiveSettings
639
774
  );
640
775
  const stream = new ReadableStream({
641
776
  start: (controller) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-sdk-provider-codex-cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "AI SDK v5 provider for OpenAI Codex CLI with native JSON Schema support",
5
5
  "keywords": [
6
6
  "ai-sdk",