ai-sdk-provider-codex-cli 0.2.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 +104 -2
- package/dist/index.cjs +532 -96
- package/dist/index.d.cts +131 -1
- package/dist/index.d.ts +131 -1
- package/dist/index.js +532 -96
- package/package.json +2 -2
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,55 +237,150 @@ var CodexCliLanguageModel = class {
|
|
|
199
237
|
const warn = validateModelId(this.modelId);
|
|
200
238
|
if (warn) this.logger.warn(`Codex CLI model: ${warn}`);
|
|
201
239
|
}
|
|
202
|
-
|
|
203
|
-
|
|
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
|
+
}
|
|
255
|
+
// Codex JSONL items use `type` for the item discriminator, but some
|
|
256
|
+
// earlier fixtures (and defensive parsing) might still surface `item_type`.
|
|
257
|
+
// This helper returns whichever is present.
|
|
258
|
+
getItemType(item) {
|
|
259
|
+
if (!item) return void 0;
|
|
260
|
+
const data = item;
|
|
261
|
+
const legacy = typeof data.item_type === "string" ? data.item_type : void 0;
|
|
262
|
+
const current = typeof data.type === "string" ? data.type : void 0;
|
|
263
|
+
return legacy ?? current;
|
|
264
|
+
}
|
|
265
|
+
buildArgs(promptText, responseFormat, settings = this.settings) {
|
|
266
|
+
const base = resolveCodexPath(settings.codexPath, settings.allowNpx);
|
|
204
267
|
const args = [...base.args, "exec", "--experimental-json"];
|
|
205
|
-
if (
|
|
268
|
+
if (settings.fullAuto) {
|
|
206
269
|
args.push("--full-auto");
|
|
207
|
-
} else if (
|
|
270
|
+
} else if (settings.dangerouslyBypassApprovalsAndSandbox) {
|
|
208
271
|
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
209
272
|
} else {
|
|
210
|
-
const approval =
|
|
273
|
+
const approval = settings.approvalMode ?? "on-failure";
|
|
211
274
|
args.push("-c", `approval_policy=${approval}`);
|
|
212
|
-
const sandbox =
|
|
275
|
+
const sandbox = settings.sandboxMode ?? "workspace-write";
|
|
213
276
|
args.push("-c", `sandbox_mode=${sandbox}`);
|
|
214
277
|
}
|
|
215
|
-
if (
|
|
278
|
+
if (settings.skipGitRepoCheck !== false) {
|
|
216
279
|
args.push("--skip-git-repo-check");
|
|
217
280
|
}
|
|
218
|
-
if (
|
|
219
|
-
args.push("
|
|
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);
|
|
220
307
|
}
|
|
221
308
|
if (this.modelId) {
|
|
222
309
|
args.push("-m", this.modelId);
|
|
223
310
|
}
|
|
311
|
+
if (settings.configOverrides) {
|
|
312
|
+
for (const [key, value] of Object.entries(settings.configOverrides)) {
|
|
313
|
+
this.addConfigOverride(args, key, value);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
224
316
|
let schemaPath;
|
|
225
317
|
if (responseFormat?.type === "json" && responseFormat.schema) {
|
|
226
|
-
const dir = mkdtempSync(join(tmpdir(), "codex-schema-"));
|
|
227
|
-
schemaPath = join(dir, "schema.json");
|
|
228
318
|
const schema = typeof responseFormat.schema === "object" ? responseFormat.schema : {};
|
|
229
319
|
const sanitizedSchema = this.sanitizeJsonSchema(schema);
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
320
|
+
const hasProperties = Object.keys(sanitizedSchema).length > 0;
|
|
321
|
+
if (hasProperties) {
|
|
322
|
+
const dir = mkdtempSync(join(tmpdir(), "codex-schema-"));
|
|
323
|
+
schemaPath = join(dir, "schema.json");
|
|
324
|
+
const schemaWithAdditional = {
|
|
325
|
+
...sanitizedSchema,
|
|
326
|
+
additionalProperties: false
|
|
327
|
+
};
|
|
328
|
+
writeFileSync(schemaPath, JSON.stringify(schemaWithAdditional, null, 2));
|
|
329
|
+
args.push("--output-schema", schemaPath);
|
|
330
|
+
}
|
|
237
331
|
}
|
|
238
332
|
args.push(promptText);
|
|
239
333
|
const env = {
|
|
240
334
|
...process.env,
|
|
241
|
-
...
|
|
335
|
+
...settings.env || {},
|
|
242
336
|
RUST_LOG: process.env.RUST_LOG || "error"
|
|
243
337
|
};
|
|
244
|
-
let lastMessagePath =
|
|
338
|
+
let lastMessagePath = settings.outputLastMessageFile;
|
|
245
339
|
if (!lastMessagePath) {
|
|
246
340
|
const dir = mkdtempSync(join(tmpdir(), "codex-cli-"));
|
|
247
341
|
lastMessagePath = join(dir, "last-message.txt");
|
|
248
342
|
}
|
|
249
343
|
args.push("--output-last-message", lastMessagePath);
|
|
250
|
-
return { cmd: base.cmd, args, env, cwd:
|
|
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]";
|
|
251
384
|
}
|
|
252
385
|
sanitizeJsonSchema(value) {
|
|
253
386
|
if (typeof value !== "object" || value === null) {
|
|
@@ -297,34 +430,183 @@ var CodexCliLanguageModel = class {
|
|
|
297
430
|
}
|
|
298
431
|
parseExperimentalJsonEvent(line) {
|
|
299
432
|
try {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
433
|
+
return JSON.parse(line);
|
|
434
|
+
} catch {
|
|
435
|
+
return void 0;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
extractUsage(evt) {
|
|
439
|
+
const reported = evt.usage;
|
|
440
|
+
if (!reported) return void 0;
|
|
441
|
+
const inputTokens = reported.input_tokens ?? 0;
|
|
442
|
+
const outputTokens = reported.output_tokens ?? 0;
|
|
443
|
+
const cachedInputTokens = reported.cached_input_tokens ?? 0;
|
|
444
|
+
return {
|
|
445
|
+
inputTokens,
|
|
446
|
+
outputTokens,
|
|
447
|
+
// totalTokens should not double-count cached tokens; track cached separately
|
|
448
|
+
totalTokens: inputTokens + outputTokens,
|
|
449
|
+
cachedInputTokens
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
getToolName(item) {
|
|
453
|
+
if (!item) return void 0;
|
|
454
|
+
const itemType = this.getItemType(item);
|
|
455
|
+
switch (itemType) {
|
|
456
|
+
case "command_execution":
|
|
457
|
+
return "exec";
|
|
458
|
+
case "file_change":
|
|
459
|
+
return "patch";
|
|
460
|
+
case "mcp_tool_call": {
|
|
461
|
+
const tool = item.tool;
|
|
462
|
+
if (typeof tool === "string" && tool.length > 0) return tool;
|
|
463
|
+
return "mcp_tool";
|
|
464
|
+
}
|
|
465
|
+
case "web_search":
|
|
466
|
+
return "web_search";
|
|
467
|
+
default:
|
|
468
|
+
return void 0;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
buildToolInputPayload(item) {
|
|
472
|
+
if (!item) return void 0;
|
|
473
|
+
const data = item;
|
|
474
|
+
switch (this.getItemType(item)) {
|
|
475
|
+
case "command_execution": {
|
|
476
|
+
const payload = {};
|
|
477
|
+
if (typeof data.command === "string") payload.command = data.command;
|
|
478
|
+
if (typeof data.status === "string") payload.status = data.status;
|
|
479
|
+
if (typeof data.cwd === "string") payload.cwd = data.cwd;
|
|
480
|
+
return Object.keys(payload).length ? payload : void 0;
|
|
481
|
+
}
|
|
482
|
+
case "file_change": {
|
|
483
|
+
const payload = {};
|
|
484
|
+
if (Array.isArray(data.changes)) payload.changes = data.changes;
|
|
485
|
+
if (typeof data.status === "string") payload.status = data.status;
|
|
486
|
+
return Object.keys(payload).length ? payload : void 0;
|
|
487
|
+
}
|
|
488
|
+
case "mcp_tool_call": {
|
|
489
|
+
const payload = {};
|
|
490
|
+
if (typeof data.server === "string") payload.server = data.server;
|
|
491
|
+
if (typeof data.tool === "string") payload.tool = data.tool;
|
|
492
|
+
if (typeof data.status === "string") payload.status = data.status;
|
|
493
|
+
if (data.arguments !== void 0) payload.arguments = data.arguments;
|
|
494
|
+
return Object.keys(payload).length ? payload : void 0;
|
|
495
|
+
}
|
|
496
|
+
case "web_search": {
|
|
497
|
+
const payload = {};
|
|
498
|
+
if (typeof data.query === "string") payload.query = data.query;
|
|
499
|
+
return Object.keys(payload).length ? payload : void 0;
|
|
500
|
+
}
|
|
501
|
+
default:
|
|
502
|
+
return void 0;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
buildToolResultPayload(item) {
|
|
506
|
+
if (!item) return { result: {} };
|
|
507
|
+
const data = item;
|
|
508
|
+
const metadata = {};
|
|
509
|
+
const itemType = this.getItemType(item);
|
|
510
|
+
if (typeof itemType === "string") metadata.itemType = itemType;
|
|
511
|
+
if (typeof item.id === "string") metadata.itemId = item.id;
|
|
512
|
+
if (typeof data.status === "string") metadata.status = data.status;
|
|
513
|
+
const buildResult = (result) => ({
|
|
514
|
+
result,
|
|
515
|
+
metadata: Object.keys(metadata).length ? metadata : void 0
|
|
516
|
+
});
|
|
517
|
+
switch (itemType) {
|
|
518
|
+
case "command_execution": {
|
|
519
|
+
const result = {};
|
|
520
|
+
if (typeof data.command === "string") result.command = data.command;
|
|
521
|
+
if (typeof data.aggregated_output === "string")
|
|
522
|
+
result.aggregatedOutput = data.aggregated_output;
|
|
523
|
+
if (typeof data.exit_code === "number") result.exitCode = data.exit_code;
|
|
524
|
+
if (typeof data.status === "string") result.status = data.status;
|
|
525
|
+
return buildResult(result);
|
|
526
|
+
}
|
|
527
|
+
case "file_change": {
|
|
528
|
+
const result = {};
|
|
529
|
+
if (Array.isArray(data.changes)) result.changes = data.changes;
|
|
530
|
+
if (typeof data.status === "string") result.status = data.status;
|
|
531
|
+
return buildResult(result);
|
|
532
|
+
}
|
|
533
|
+
case "mcp_tool_call": {
|
|
534
|
+
const result = {};
|
|
535
|
+
if (typeof data.server === "string") {
|
|
536
|
+
result.server = data.server;
|
|
537
|
+
metadata.server = data.server;
|
|
538
|
+
}
|
|
539
|
+
if (typeof data.tool === "string") result.tool = data.tool;
|
|
540
|
+
if (typeof data.status === "string") result.status = data.status;
|
|
541
|
+
if (data.result !== void 0) result.result = data.result;
|
|
542
|
+
if (data.error !== void 0) result.error = data.error;
|
|
543
|
+
return buildResult(result);
|
|
544
|
+
}
|
|
545
|
+
case "web_search": {
|
|
546
|
+
const result = {};
|
|
547
|
+
if (typeof data.query === "string") result.query = data.query;
|
|
548
|
+
if (typeof data.status === "string") result.status = data.status;
|
|
549
|
+
return buildResult(result);
|
|
322
550
|
}
|
|
323
|
-
|
|
551
|
+
default: {
|
|
552
|
+
const result = { ...data };
|
|
553
|
+
return buildResult(result);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
safeStringify(value) {
|
|
558
|
+
if (value === void 0) return "";
|
|
559
|
+
if (typeof value === "string") return value;
|
|
560
|
+
try {
|
|
561
|
+
return JSON.stringify(value);
|
|
324
562
|
} catch {
|
|
325
|
-
return
|
|
563
|
+
return "";
|
|
326
564
|
}
|
|
327
565
|
}
|
|
566
|
+
emitToolInvocation(controller, toolCallId, toolName, inputPayload) {
|
|
567
|
+
const inputString = this.safeStringify(inputPayload);
|
|
568
|
+
controller.enqueue({ type: "tool-input-start", id: toolCallId, toolName });
|
|
569
|
+
if (inputString) {
|
|
570
|
+
controller.enqueue({ type: "tool-input-delta", id: toolCallId, delta: inputString });
|
|
571
|
+
}
|
|
572
|
+
controller.enqueue({ type: "tool-input-end", id: toolCallId });
|
|
573
|
+
controller.enqueue({
|
|
574
|
+
type: "tool-call",
|
|
575
|
+
toolCallId,
|
|
576
|
+
toolName,
|
|
577
|
+
input: inputString,
|
|
578
|
+
providerExecuted: true
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
emitToolResult(controller, toolCallId, toolName, item, resultPayload, metadata) {
|
|
582
|
+
const providerMetadataEntries = {
|
|
583
|
+
...metadata ?? {}
|
|
584
|
+
};
|
|
585
|
+
const itemType = this.getItemType(item);
|
|
586
|
+
if (itemType && providerMetadataEntries.itemType === void 0) {
|
|
587
|
+
providerMetadataEntries.itemType = itemType;
|
|
588
|
+
}
|
|
589
|
+
if (item.id && providerMetadataEntries.itemId === void 0) {
|
|
590
|
+
providerMetadataEntries.itemId = item.id;
|
|
591
|
+
}
|
|
592
|
+
let isError;
|
|
593
|
+
if (itemType === "command_execution") {
|
|
594
|
+
const data = item;
|
|
595
|
+
const exitCode = typeof data.exit_code === "number" ? data.exit_code : void 0;
|
|
596
|
+
const status = typeof data.status === "string" ? data.status : void 0;
|
|
597
|
+
if (exitCode !== void 0 && exitCode !== 0 || status === "failed") {
|
|
598
|
+
isError = true;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
controller.enqueue({
|
|
602
|
+
type: "tool-result",
|
|
603
|
+
toolCallId,
|
|
604
|
+
toolName,
|
|
605
|
+
result: resultPayload ?? {},
|
|
606
|
+
...isError ? { isError: true } : {},
|
|
607
|
+
...Object.keys(providerMetadataEntries).length ? { providerMetadata: { "codex-cli": providerMetadataEntries } } : {}
|
|
608
|
+
});
|
|
609
|
+
}
|
|
328
610
|
handleSpawnError(err, promptExcerpt) {
|
|
329
611
|
const e = err && typeof err === "object" ? err : void 0;
|
|
330
612
|
const message = String((e?.message ?? err) || "Failed to run Codex CLI");
|
|
@@ -346,10 +628,17 @@ var CodexCliLanguageModel = class {
|
|
|
346
628
|
...this.mapWarnings(options),
|
|
347
629
|
...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
|
|
348
630
|
];
|
|
631
|
+
const providerOptions = await parseProviderOptions({
|
|
632
|
+
provider: this.provider,
|
|
633
|
+
providerOptions: options.providerOptions,
|
|
634
|
+
schema: codexCliProviderOptionsSchema
|
|
635
|
+
});
|
|
636
|
+
const effectiveSettings = this.mergeSettings(providerOptions);
|
|
349
637
|
const responseFormat = options.responseFormat?.type === "json" ? { type: "json", schema: options.responseFormat.schema } : void 0;
|
|
350
638
|
const { cmd, args, env, cwd, lastMessagePath, schemaPath } = this.buildArgs(
|
|
351
639
|
promptText,
|
|
352
|
-
responseFormat
|
|
640
|
+
responseFormat,
|
|
641
|
+
effectiveSettings
|
|
353
642
|
);
|
|
354
643
|
let text = "";
|
|
355
644
|
const usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
@@ -367,25 +656,56 @@ var CodexCliLanguageModel = class {
|
|
|
367
656
|
try {
|
|
368
657
|
await new Promise((resolve, reject) => {
|
|
369
658
|
let stderr = "";
|
|
659
|
+
let turnFailureMessage;
|
|
370
660
|
child.stderr.on("data", (d) => stderr += String(d));
|
|
371
661
|
child.stdout.setEncoding("utf8");
|
|
372
662
|
child.stdout.on("data", (chunk) => {
|
|
373
663
|
const lines = chunk.split(/\r?\n/).filter(Boolean);
|
|
374
664
|
for (const line of lines) {
|
|
375
|
-
const
|
|
376
|
-
if (
|
|
377
|
-
if (
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
665
|
+
const event = this.parseExperimentalJsonEvent(line);
|
|
666
|
+
if (!event) continue;
|
|
667
|
+
if (event.type === "thread.started" && typeof event.thread_id === "string") {
|
|
668
|
+
this.sessionId = event.thread_id;
|
|
669
|
+
}
|
|
670
|
+
if (event.type === "session.created" && typeof event.session_id === "string") {
|
|
671
|
+
this.sessionId = event.session_id;
|
|
672
|
+
}
|
|
673
|
+
if (event.type === "turn.completed") {
|
|
674
|
+
const usageEvent = this.extractUsage(event);
|
|
675
|
+
if (usageEvent) {
|
|
676
|
+
usage.inputTokens = usageEvent.inputTokens;
|
|
677
|
+
usage.outputTokens = usageEvent.outputTokens;
|
|
678
|
+
usage.totalTokens = usageEvent.totalTokens;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (event.type === "item.completed" && this.getItemType(event.item) === "assistant_message" && typeof event.item?.text === "string") {
|
|
682
|
+
text = event.item.text;
|
|
683
|
+
}
|
|
684
|
+
if (event.type === "turn.failed") {
|
|
685
|
+
const errorText = event.error && typeof event.error.message === "string" && event.error.message || (typeof event.message === "string" ? event.message : void 0);
|
|
686
|
+
turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex turn failed";
|
|
687
|
+
}
|
|
688
|
+
if (event.type === "error") {
|
|
689
|
+
const errorText = typeof event.message === "string" ? event.message : void 0;
|
|
690
|
+
turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex error";
|
|
382
691
|
}
|
|
383
692
|
}
|
|
384
693
|
});
|
|
385
694
|
child.on("error", (e) => reject(this.handleSpawnError(e, promptExcerpt)));
|
|
386
695
|
child.on("close", (code) => {
|
|
387
|
-
if (code === 0)
|
|
388
|
-
|
|
696
|
+
if (code === 0) {
|
|
697
|
+
if (turnFailureMessage) {
|
|
698
|
+
reject(
|
|
699
|
+
createAPICallError({
|
|
700
|
+
message: turnFailureMessage,
|
|
701
|
+
stderr,
|
|
702
|
+
promptExcerpt
|
|
703
|
+
})
|
|
704
|
+
);
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
resolve();
|
|
708
|
+
} else {
|
|
389
709
|
reject(
|
|
390
710
|
createAPICallError({
|
|
391
711
|
message: `Codex CLI exited with code ${code}`,
|
|
@@ -394,6 +714,7 @@ var CodexCliLanguageModel = class {
|
|
|
394
714
|
promptExcerpt
|
|
395
715
|
})
|
|
396
716
|
);
|
|
717
|
+
}
|
|
397
718
|
});
|
|
398
719
|
});
|
|
399
720
|
} finally {
|
|
@@ -439,10 +760,17 @@ var CodexCliLanguageModel = class {
|
|
|
439
760
|
...this.mapWarnings(options),
|
|
440
761
|
...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
|
|
441
762
|
];
|
|
763
|
+
const providerOptions = await parseProviderOptions({
|
|
764
|
+
provider: this.provider,
|
|
765
|
+
providerOptions: options.providerOptions,
|
|
766
|
+
schema: codexCliProviderOptionsSchema
|
|
767
|
+
});
|
|
768
|
+
const effectiveSettings = this.mergeSettings(providerOptions);
|
|
442
769
|
const responseFormat = options.responseFormat?.type === "json" ? { type: "json", schema: options.responseFormat.schema } : void 0;
|
|
443
770
|
const { cmd, args, env, cwd, lastMessagePath, schemaPath } = this.buildArgs(
|
|
444
771
|
promptText,
|
|
445
|
-
responseFormat
|
|
772
|
+
responseFormat,
|
|
773
|
+
effectiveSettings
|
|
446
774
|
);
|
|
447
775
|
const stream = new ReadableStream({
|
|
448
776
|
start: (controller) => {
|
|
@@ -450,6 +778,69 @@ var CodexCliLanguageModel = class {
|
|
|
450
778
|
controller.enqueue({ type: "stream-start", warnings });
|
|
451
779
|
let stderr = "";
|
|
452
780
|
let accumulatedText = "";
|
|
781
|
+
const activeTools = /* @__PURE__ */ new Map();
|
|
782
|
+
let responseMetadataSent = false;
|
|
783
|
+
let lastUsage;
|
|
784
|
+
let turnFailureMessage;
|
|
785
|
+
const sendMetadata = (meta = {}) => {
|
|
786
|
+
controller.enqueue({
|
|
787
|
+
type: "response-metadata",
|
|
788
|
+
id: randomUUID(),
|
|
789
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
790
|
+
modelId: this.modelId,
|
|
791
|
+
...Object.keys(meta).length ? { providerMetadata: { "codex-cli": meta } } : {}
|
|
792
|
+
});
|
|
793
|
+
};
|
|
794
|
+
const handleItemEvent = (event) => {
|
|
795
|
+
const item = event.item;
|
|
796
|
+
if (!item) return;
|
|
797
|
+
if (event.type === "item.completed" && this.getItemType(item) === "assistant_message" && typeof item.text === "string") {
|
|
798
|
+
accumulatedText = item.text;
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
const toolName = this.getToolName(item);
|
|
802
|
+
if (!toolName) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
const mapKey = typeof item.id === "string" && item.id.length > 0 ? item.id : randomUUID();
|
|
806
|
+
let toolState = activeTools.get(mapKey);
|
|
807
|
+
const latestInput = this.buildToolInputPayload(item);
|
|
808
|
+
if (!toolState) {
|
|
809
|
+
toolState = {
|
|
810
|
+
toolCallId: mapKey,
|
|
811
|
+
toolName,
|
|
812
|
+
inputPayload: latestInput,
|
|
813
|
+
hasEmittedCall: false
|
|
814
|
+
};
|
|
815
|
+
activeTools.set(mapKey, toolState);
|
|
816
|
+
} else {
|
|
817
|
+
toolState.toolName = toolName;
|
|
818
|
+
if (latestInput !== void 0) {
|
|
819
|
+
toolState.inputPayload = latestInput;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (!toolState.hasEmittedCall) {
|
|
823
|
+
this.emitToolInvocation(
|
|
824
|
+
controller,
|
|
825
|
+
toolState.toolCallId,
|
|
826
|
+
toolState.toolName,
|
|
827
|
+
toolState.inputPayload
|
|
828
|
+
);
|
|
829
|
+
toolState.hasEmittedCall = true;
|
|
830
|
+
}
|
|
831
|
+
if (event.type === "item.completed") {
|
|
832
|
+
const { result, metadata } = this.buildToolResultPayload(item);
|
|
833
|
+
this.emitToolResult(
|
|
834
|
+
controller,
|
|
835
|
+
toolState.toolCallId,
|
|
836
|
+
toolState.toolName,
|
|
837
|
+
item,
|
|
838
|
+
result,
|
|
839
|
+
metadata
|
|
840
|
+
);
|
|
841
|
+
activeTools.delete(mapKey);
|
|
842
|
+
}
|
|
843
|
+
};
|
|
453
844
|
const onAbort = () => {
|
|
454
845
|
child.kill("SIGTERM");
|
|
455
846
|
};
|
|
@@ -461,42 +852,7 @@ var CodexCliLanguageModel = class {
|
|
|
461
852
|
}
|
|
462
853
|
options.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
463
854
|
}
|
|
464
|
-
|
|
465
|
-
child.stdout.setEncoding("utf8");
|
|
466
|
-
child.stdout.on("data", (chunk) => {
|
|
467
|
-
const lines = chunk.split(/\r?\n/).filter(Boolean);
|
|
468
|
-
for (const line of lines) {
|
|
469
|
-
const parsed = this.parseExperimentalJsonEvent(line);
|
|
470
|
-
if (parsed.sessionId) {
|
|
471
|
-
this.sessionId = parsed.sessionId;
|
|
472
|
-
controller.enqueue({
|
|
473
|
-
type: "response-metadata",
|
|
474
|
-
id: randomUUID(),
|
|
475
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
476
|
-
modelId: this.modelId
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
if (parsed.text) {
|
|
480
|
-
accumulatedText = parsed.text;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
});
|
|
484
|
-
const cleanupSchema = () => {
|
|
485
|
-
if (!schemaPath) return;
|
|
486
|
-
try {
|
|
487
|
-
const schemaDir = dirname(schemaPath);
|
|
488
|
-
rmSync(schemaDir, { recursive: true, force: true });
|
|
489
|
-
} catch {
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
child.on("error", (e) => {
|
|
493
|
-
if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
|
|
494
|
-
cleanupSchema();
|
|
495
|
-
controller.error(this.handleSpawnError(e, promptExcerpt));
|
|
496
|
-
});
|
|
497
|
-
child.on("close", (code) => {
|
|
498
|
-
if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
|
|
499
|
-
cleanupSchema();
|
|
855
|
+
const finishStream = (code) => {
|
|
500
856
|
if (code !== 0) {
|
|
501
857
|
controller.error(
|
|
502
858
|
createAPICallError({
|
|
@@ -508,6 +864,16 @@ var CodexCliLanguageModel = class {
|
|
|
508
864
|
);
|
|
509
865
|
return;
|
|
510
866
|
}
|
|
867
|
+
if (turnFailureMessage) {
|
|
868
|
+
controller.error(
|
|
869
|
+
createAPICallError({
|
|
870
|
+
message: turnFailureMessage,
|
|
871
|
+
stderr,
|
|
872
|
+
promptExcerpt
|
|
873
|
+
})
|
|
874
|
+
);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
511
877
|
let finalText = accumulatedText;
|
|
512
878
|
if (!finalText && lastMessagePath) {
|
|
513
879
|
try {
|
|
@@ -521,14 +887,84 @@ var CodexCliLanguageModel = class {
|
|
|
521
887
|
}
|
|
522
888
|
}
|
|
523
889
|
if (finalText) {
|
|
524
|
-
|
|
890
|
+
const textId = randomUUID();
|
|
891
|
+
controller.enqueue({ type: "text-start", id: textId });
|
|
892
|
+
controller.enqueue({ type: "text-delta", id: textId, delta: finalText });
|
|
893
|
+
controller.enqueue({ type: "text-end", id: textId });
|
|
525
894
|
}
|
|
895
|
+
const usageSummary = lastUsage ?? { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
526
896
|
controller.enqueue({
|
|
527
897
|
type: "finish",
|
|
528
898
|
finishReason: "stop",
|
|
529
|
-
usage:
|
|
899
|
+
usage: usageSummary
|
|
530
900
|
});
|
|
531
901
|
controller.close();
|
|
902
|
+
};
|
|
903
|
+
child.stderr.on("data", (d) => stderr += String(d));
|
|
904
|
+
child.stdout.setEncoding("utf8");
|
|
905
|
+
child.stdout.on("data", (chunk) => {
|
|
906
|
+
const lines = chunk.split(/\r?\n/).filter(Boolean);
|
|
907
|
+
for (const line of lines) {
|
|
908
|
+
const event = this.parseExperimentalJsonEvent(line);
|
|
909
|
+
if (!event) continue;
|
|
910
|
+
if (event.type === "thread.started" && typeof event.thread_id === "string") {
|
|
911
|
+
this.sessionId = event.thread_id;
|
|
912
|
+
if (!responseMetadataSent) {
|
|
913
|
+
responseMetadataSent = true;
|
|
914
|
+
sendMetadata();
|
|
915
|
+
}
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
if (event.type === "session.created" && typeof event.session_id === "string") {
|
|
919
|
+
this.sessionId = event.session_id;
|
|
920
|
+
if (!responseMetadataSent) {
|
|
921
|
+
responseMetadataSent = true;
|
|
922
|
+
sendMetadata();
|
|
923
|
+
}
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
if (event.type === "turn.completed") {
|
|
927
|
+
const usageEvent = this.extractUsage(event);
|
|
928
|
+
if (usageEvent) {
|
|
929
|
+
lastUsage = usageEvent;
|
|
930
|
+
}
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
if (event.type === "turn.failed") {
|
|
934
|
+
const errorText = event.error && typeof event.error.message === "string" && event.error.message || (typeof event.message === "string" ? event.message : void 0);
|
|
935
|
+
turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex turn failed";
|
|
936
|
+
sendMetadata({ error: turnFailureMessage });
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
if (event.type === "error") {
|
|
940
|
+
const errorText = typeof event.message === "string" ? event.message : void 0;
|
|
941
|
+
const effective = errorText ?? "Codex error";
|
|
942
|
+
turnFailureMessage = turnFailureMessage ?? effective;
|
|
943
|
+
sendMetadata({ error: effective });
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
if (event.type && event.type.startsWith("item.")) {
|
|
947
|
+
handleItemEvent(event);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
const cleanupSchema = () => {
|
|
952
|
+
if (!schemaPath) return;
|
|
953
|
+
try {
|
|
954
|
+
const schemaDir = dirname(schemaPath);
|
|
955
|
+
rmSync(schemaDir, { recursive: true, force: true });
|
|
956
|
+
} catch {
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
child.on("error", (e) => {
|
|
960
|
+
if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
|
|
961
|
+
cleanupSchema();
|
|
962
|
+
controller.error(this.handleSpawnError(e, promptExcerpt));
|
|
963
|
+
});
|
|
964
|
+
child.on("close", (code) => {
|
|
965
|
+
if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
|
|
966
|
+
cleanupSchema();
|
|
967
|
+
setImmediate(() => finishStream(code));
|
|
532
968
|
});
|
|
533
969
|
},
|
|
534
970
|
cancel: () => {
|