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