@just-every/ensemble 0.2.245 → 0.2.247
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/dist/cjs/data/model_data.cjs +218 -156
- package/dist/cjs/data/model_data.d.ts.map +1 -1
- package/dist/cjs/data/model_data.js.map +1 -1
- package/dist/cjs/model_providers/claude.cjs +8 -7
- package/dist/cjs/model_providers/claude.d.ts.map +1 -1
- package/dist/cjs/model_providers/claude.js.map +1 -1
- package/dist/cjs/model_providers/codex.cjs +660 -21
- package/dist/cjs/model_providers/codex.d.ts.map +1 -1
- package/dist/cjs/model_providers/codex.js.map +1 -1
- package/dist/cjs/model_providers/model_provider.cjs +11 -0
- package/dist/cjs/model_providers/model_provider.d.ts.map +1 -1
- package/dist/cjs/model_providers/model_provider.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/types/types.d.ts +4 -0
- package/dist/cjs/types/types.d.ts.map +1 -1
- package/dist/data/model_data.d.ts.map +1 -1
- package/dist/data/model_data.js +218 -156
- package/dist/data/model_data.js.map +1 -1
- package/dist/model_providers/claude.d.ts.map +1 -1
- package/dist/model_providers/claude.js +8 -7
- package/dist/model_providers/claude.js.map +1 -1
- package/dist/model_providers/codex.d.ts.map +1 -1
- package/dist/model_providers/codex.js +628 -22
- package/dist/model_providers/codex.js.map +1 -1
- package/dist/model_providers/model_provider.d.ts.map +1 -1
- package/dist/model_providers/model_provider.js +12 -1
- package/dist/model_providers/model_provider.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/types.d.ts +4 -0
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
-
import {
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { chmod, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from 'fs/promises';
|
|
3
4
|
import { tmpdir, homedir } from 'os';
|
|
4
5
|
import path from 'path';
|
|
6
|
+
import { setTimeout as sleep } from 'timers/promises';
|
|
5
7
|
import { BaseModelProvider } from './base_provider.js';
|
|
6
8
|
import { costTracker } from '../utils/cost_tracker.js';
|
|
7
9
|
import { log_llm_error, log_llm_request, log_llm_response } from '../utils/llm_logger.js';
|
|
@@ -183,7 +185,7 @@ function summarizeCodexExecStream(value) {
|
|
|
183
185
|
truncated: true,
|
|
184
186
|
};
|
|
185
187
|
}
|
|
186
|
-
function codexUsageFromJsonl(stdout, model, requestId) {
|
|
188
|
+
function codexUsageFromJsonl(stdout, model, requestId, metadata) {
|
|
187
189
|
let latestUsage;
|
|
188
190
|
for (const line of stdout.split(/\r?\n/)) {
|
|
189
191
|
const trimmed = line.trim();
|
|
@@ -221,6 +223,7 @@ function codexUsageFromJsonl(stdout, model, requestId) {
|
|
|
221
223
|
cached_tokens: latestUsage.cached_input_tokens,
|
|
222
224
|
request_id: requestId,
|
|
223
225
|
metadata: {
|
|
226
|
+
...metadata,
|
|
224
227
|
source: 'codex_cli_json',
|
|
225
228
|
reasoning_output_tokens: latestUsage.reasoning_output_tokens ?? 0,
|
|
226
229
|
},
|
|
@@ -232,6 +235,482 @@ function finiteNumber(value) {
|
|
|
232
235
|
function isRecord(value) {
|
|
233
236
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
234
237
|
}
|
|
238
|
+
function getCodexProviderSettings(settings) {
|
|
239
|
+
return settings;
|
|
240
|
+
}
|
|
241
|
+
function compactJson(value) {
|
|
242
|
+
return JSON.stringify(value);
|
|
243
|
+
}
|
|
244
|
+
function getToolNames(tools) {
|
|
245
|
+
return tools.map(tool => tool.definition.function.name);
|
|
246
|
+
}
|
|
247
|
+
function getTerminalToolNameSet(agent) {
|
|
248
|
+
return new Set((agent.terminalToolNames ?? []).filter((name) => typeof name === 'string' && name.trim().length > 0));
|
|
249
|
+
}
|
|
250
|
+
function getAvailableTerminalToolNames(tools, agent) {
|
|
251
|
+
const terminalNames = getTerminalToolNameSet(agent);
|
|
252
|
+
if (terminalNames.size === 0)
|
|
253
|
+
return [];
|
|
254
|
+
return getToolNames(tools).filter(name => terminalNames.has(name));
|
|
255
|
+
}
|
|
256
|
+
function createCodexToolOutputSchema(tools, agent) {
|
|
257
|
+
const requiresTerminalTool = getAvailableTerminalToolNames(tools, agent).length > 0;
|
|
258
|
+
return {
|
|
259
|
+
name: 'codex_tool_action',
|
|
260
|
+
type: 'json_schema',
|
|
261
|
+
description: 'A simulated Ensemble tool action for Codex CLI provider requests.',
|
|
262
|
+
schema: {
|
|
263
|
+
type: 'object',
|
|
264
|
+
additionalProperties: false,
|
|
265
|
+
properties: {
|
|
266
|
+
action: {
|
|
267
|
+
type: 'string',
|
|
268
|
+
enum: requiresTerminalTool ? ['tool_calls'] : ['tool_calls', 'final_response'],
|
|
269
|
+
description: requiresTerminalTool
|
|
270
|
+
? 'Use tool_calls. This request is only complete after calling a terminal tool.'
|
|
271
|
+
: 'Use tool_calls when requesting Ensemble tool execution; use final_response only when no tool is needed.',
|
|
272
|
+
},
|
|
273
|
+
toolCalls: {
|
|
274
|
+
type: 'array',
|
|
275
|
+
description: 'Tool calls to execute in parallel for this turn. Empty when action is final_response.',
|
|
276
|
+
items: {
|
|
277
|
+
type: 'object',
|
|
278
|
+
additionalProperties: false,
|
|
279
|
+
properties: {
|
|
280
|
+
name: {
|
|
281
|
+
type: 'string',
|
|
282
|
+
enum: getToolNames(tools),
|
|
283
|
+
},
|
|
284
|
+
argumentsJson: {
|
|
285
|
+
type: 'string',
|
|
286
|
+
description: 'A compact JSON string containing the complete arguments object for the named tool.',
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
required: ['name', 'argumentsJson'],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
finalResponse: {
|
|
293
|
+
type: 'string',
|
|
294
|
+
description: 'Final assistant response. Use an empty string when action is tool_calls.',
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
required: ['action', 'toolCalls', 'finalResponse'],
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function describeCodexToolChoice(settings) {
|
|
302
|
+
const toolChoice = settings?.tool_choice;
|
|
303
|
+
if (toolChoice === 'required') {
|
|
304
|
+
return 'This turn requires at least one tool call.';
|
|
305
|
+
}
|
|
306
|
+
if (toolChoice === 'none') {
|
|
307
|
+
return 'This turn forbids tool calls; return final_response.';
|
|
308
|
+
}
|
|
309
|
+
if (typeof toolChoice === 'object' && toolChoice?.type === 'function' && toolChoice.function?.name) {
|
|
310
|
+
return `This turn requires the ${toolChoice.function.name} tool.`;
|
|
311
|
+
}
|
|
312
|
+
return 'Use tool_calls when a tool result is needed; otherwise use final_response.';
|
|
313
|
+
}
|
|
314
|
+
function buildCodexToolInstructions(tools, agent) {
|
|
315
|
+
const settings = agent.modelSettings;
|
|
316
|
+
const terminalToolNames = getAvailableTerminalToolNames(tools, agent);
|
|
317
|
+
const toolDescriptions = tools
|
|
318
|
+
.map(tool => {
|
|
319
|
+
const fn = tool.definition.function;
|
|
320
|
+
return [
|
|
321
|
+
`Tool: ${fn.name}`,
|
|
322
|
+
`Description: ${fn.description}`,
|
|
323
|
+
`Parameters JSON schema: ${compactJson(fn.parameters)}`,
|
|
324
|
+
].join('\n');
|
|
325
|
+
})
|
|
326
|
+
.join('\n\n');
|
|
327
|
+
return [
|
|
328
|
+
'You are using Ensemble simulated tool mode.',
|
|
329
|
+
'You cannot execute these tools yourself. Instead, respond only with JSON matching the provided output schema.',
|
|
330
|
+
describeCodexToolChoice(settings),
|
|
331
|
+
'For action "tool_calls", include one or more entries in toolCalls and set finalResponse to an empty string.',
|
|
332
|
+
terminalToolNames.length > 0
|
|
333
|
+
? `This request is not complete until you call one of these terminal tools: ${terminalToolNames.join(', ')}. Do not use final_response.`
|
|
334
|
+
: 'For action "final_response", set toolCalls to an empty array and put the final answer in finalResponse.',
|
|
335
|
+
'Use exact tool names.',
|
|
336
|
+
'Set argumentsJson to a compact JSON string containing the complete arguments object for the named tool.',
|
|
337
|
+
'For example, the arguments object {"label":"ok"} must be encoded as "{\\"label\\":\\"ok\\"}".',
|
|
338
|
+
'',
|
|
339
|
+
'Available tools:',
|
|
340
|
+
toolDescriptions,
|
|
341
|
+
].join('\n');
|
|
342
|
+
}
|
|
343
|
+
function validateCodexWorkspaceRelativePath(filePath) {
|
|
344
|
+
const normalized = filePath.replace(/\\/g, '/').replace(/^\.\/+/, '');
|
|
345
|
+
if (!normalized ||
|
|
346
|
+
path.isAbsolute(normalized) ||
|
|
347
|
+
normalized.split('/').some(part => part === '..' || part === '')) {
|
|
348
|
+
throw new Error(`Invalid Codex workspace file path: ${filePath}`);
|
|
349
|
+
}
|
|
350
|
+
return normalized;
|
|
351
|
+
}
|
|
352
|
+
async function writeCodexWorkspaceFiles(tempDir, files) {
|
|
353
|
+
const written = [];
|
|
354
|
+
for (const [rawPath, content] of Object.entries(files ?? {})) {
|
|
355
|
+
const relativePath = validateCodexWorkspaceRelativePath(rawPath);
|
|
356
|
+
const targetPath = path.join(tempDir, relativePath);
|
|
357
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
358
|
+
const dataUrlMatch = /^data:[^;,]+;base64,([\s\S]*)$/i.exec(content);
|
|
359
|
+
if (dataUrlMatch) {
|
|
360
|
+
await writeFile(targetPath, Buffer.from(dataUrlMatch[1] ?? '', 'base64'));
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
await writeFile(targetPath, content, 'utf8');
|
|
364
|
+
}
|
|
365
|
+
written.push(relativePath);
|
|
366
|
+
}
|
|
367
|
+
return written.sort();
|
|
368
|
+
}
|
|
369
|
+
function normalizeCodexWorkspaceFinalFiles(files) {
|
|
370
|
+
return [...new Set((files ?? []).map(validateCodexWorkspaceRelativePath))].sort();
|
|
371
|
+
}
|
|
372
|
+
async function readCodexWorkspaceFinalFiles(tempDir, files) {
|
|
373
|
+
const output = {};
|
|
374
|
+
for (const relativePath of normalizeCodexWorkspaceFinalFiles(files)) {
|
|
375
|
+
const filePath = path.join(tempDir, relativePath);
|
|
376
|
+
output[relativePath] = await readFile(filePath, 'utf8');
|
|
377
|
+
}
|
|
378
|
+
return output;
|
|
379
|
+
}
|
|
380
|
+
function appendCodexWorkspaceFinalFiles(content, files) {
|
|
381
|
+
const entries = Object.entries(files);
|
|
382
|
+
if (entries.length === 0)
|
|
383
|
+
return content;
|
|
384
|
+
return [
|
|
385
|
+
content,
|
|
386
|
+
'',
|
|
387
|
+
'<codex_workspace_files_json>',
|
|
388
|
+
JSON.stringify(Object.fromEntries(entries), null, 2),
|
|
389
|
+
'</codex_workspace_files_json>',
|
|
390
|
+
].join('\n');
|
|
391
|
+
}
|
|
392
|
+
function buildCodexWorkspaceFileInstructions(files, extra) {
|
|
393
|
+
if (files.length === 0 && !extra?.trim())
|
|
394
|
+
return '';
|
|
395
|
+
return [
|
|
396
|
+
'A disposable Codex workspace has been prepared for this request.',
|
|
397
|
+
files.length > 0
|
|
398
|
+
? `Workspace files available in the current directory:\n${files.map(file => `- ${file}`).join('\n')}`
|
|
399
|
+
: undefined,
|
|
400
|
+
extra?.trim() || undefined,
|
|
401
|
+
]
|
|
402
|
+
.filter(Boolean)
|
|
403
|
+
.join('\n');
|
|
404
|
+
}
|
|
405
|
+
function shellIdentifier(value) {
|
|
406
|
+
if (!/^[A-Za-z0-9_.:-]+$/.test(value)) {
|
|
407
|
+
throw new Error(`Codex filesystem tool name is not shell-safe: ${value}`);
|
|
408
|
+
}
|
|
409
|
+
return value;
|
|
410
|
+
}
|
|
411
|
+
function buildCodexFilesystemToolInstructions(tools, agent) {
|
|
412
|
+
const terminalToolNames = getAvailableTerminalToolNames(tools, agent);
|
|
413
|
+
const toolDescriptions = tools
|
|
414
|
+
.map(tool => {
|
|
415
|
+
const fn = tool.definition.function;
|
|
416
|
+
return [
|
|
417
|
+
`Command: ./tools/${fn.name} <arguments-json-file>`,
|
|
418
|
+
`Description: ${fn.description}`,
|
|
419
|
+
`Arguments JSON schema: ${compactJson(fn.parameters)}`,
|
|
420
|
+
].join('\n');
|
|
421
|
+
})
|
|
422
|
+
.join('\n\n');
|
|
423
|
+
return [
|
|
424
|
+
'You are using Codex filesystem tool mode.',
|
|
425
|
+
'Work in the current directory. Read the provided JSON files, write candidate JSON files, and run the executable commands in ./tools.',
|
|
426
|
+
'Each ./tools command takes one path to a JSON arguments file. The command prints the real tool result to stdout.',
|
|
427
|
+
'When a layered document argument would otherwise be a large JSON string, you may put candidateLayeredDocumentPath or repairedLayeredDocumentPath in the arguments file; the wrapper will read that file and pass the required JSON string to the real tool.',
|
|
428
|
+
terminalToolNames.length > 0
|
|
429
|
+
? `This request is not complete until you run one of these terminal tool commands: ${terminalToolNames.map(name => `./tools/${name}`).join(', ')}.`
|
|
430
|
+
: undefined,
|
|
431
|
+
terminalToolNames.length > 0
|
|
432
|
+
? 'After the terminal tool command succeeds, respond with a brief completion note. Do not invent tool outputs; use the command output.'
|
|
433
|
+
: 'Run the commands you need, then respond with a brief completion note. Do not invent command outputs; use stdout from the commands.',
|
|
434
|
+
'',
|
|
435
|
+
'Available filesystem tools:',
|
|
436
|
+
toolDescriptions,
|
|
437
|
+
]
|
|
438
|
+
.filter(Boolean)
|
|
439
|
+
.join('\n');
|
|
440
|
+
}
|
|
441
|
+
async function startCodexFilesystemToolBroker(tempDir, tools, agent) {
|
|
442
|
+
const toolsDir = path.join(tempDir, 'tools');
|
|
443
|
+
const requestDir = path.join(tempDir, '.codex-tool-requests');
|
|
444
|
+
const responseDir = path.join(tempDir, '.codex-tool-responses');
|
|
445
|
+
await Promise.all([
|
|
446
|
+
mkdir(toolsDir, { recursive: true }),
|
|
447
|
+
mkdir(requestDir, { recursive: true }),
|
|
448
|
+
mkdir(responseDir, { recursive: true }),
|
|
449
|
+
]);
|
|
450
|
+
for (const tool of tools) {
|
|
451
|
+
const toolName = shellIdentifier(tool.definition.function.name);
|
|
452
|
+
const toolPath = path.join(toolsDir, toolName);
|
|
453
|
+
await writeFile(toolPath, buildCodexFilesystemToolWrapperScript({
|
|
454
|
+
toolName,
|
|
455
|
+
workspaceDir: tempDir,
|
|
456
|
+
requestDir,
|
|
457
|
+
responseDir,
|
|
458
|
+
}), 'utf8');
|
|
459
|
+
await chmod(toolPath, 0o755);
|
|
460
|
+
}
|
|
461
|
+
const toolByName = new Map(tools.map(tool => [tool.definition.function.name, tool]));
|
|
462
|
+
const seen = new Set();
|
|
463
|
+
const inFlight = new Set();
|
|
464
|
+
let stopped = false;
|
|
465
|
+
const serviceRequest = async (fileName) => {
|
|
466
|
+
const requestPath = path.join(requestDir, fileName);
|
|
467
|
+
let request;
|
|
468
|
+
try {
|
|
469
|
+
request = JSON.parse(await readFile(requestPath, 'utf8'));
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
await writeFile(path.join(responseDir, fileName), JSON.stringify({
|
|
473
|
+
ok: false,
|
|
474
|
+
error: `Could not read Codex filesystem tool request: ${error instanceof Error ? error.message : String(error)}`,
|
|
475
|
+
}), 'utf8');
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const toolName = typeof request.toolName === 'string' ? request.toolName : '';
|
|
479
|
+
const tool = toolByName.get(toolName);
|
|
480
|
+
if (!tool) {
|
|
481
|
+
await writeFile(path.join(responseDir, fileName), JSON.stringify({ ok: false, error: `Unknown Codex filesystem tool: ${toolName || '<missing>'}` }), 'utf8');
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
const argumentsJson = typeof request.argumentsJson === 'string' ? request.argumentsJson : '{}';
|
|
486
|
+
const parsedArguments = JSON.parse(argumentsJson);
|
|
487
|
+
const result = await tool.function(parsedArguments);
|
|
488
|
+
const output = typeof result === 'string' ? result : JSON.stringify(result);
|
|
489
|
+
await writeFile(path.join(responseDir, fileName), JSON.stringify({ ok: true, output }), 'utf8');
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
await writeFile(path.join(responseDir, fileName), JSON.stringify({
|
|
493
|
+
ok: false,
|
|
494
|
+
error: error instanceof Error ? error.message : String(error),
|
|
495
|
+
}), 'utf8');
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
const loop = (async () => {
|
|
499
|
+
while (!stopped) {
|
|
500
|
+
const entries = await readdir(requestDir).catch(() => []);
|
|
501
|
+
for (const fileName of entries) {
|
|
502
|
+
if (!fileName.endsWith('.json') || seen.has(fileName))
|
|
503
|
+
continue;
|
|
504
|
+
seen.add(fileName);
|
|
505
|
+
const task = serviceRequest(fileName).finally(() => {
|
|
506
|
+
inFlight.delete(task);
|
|
507
|
+
});
|
|
508
|
+
inFlight.add(task);
|
|
509
|
+
}
|
|
510
|
+
await sleep(50);
|
|
511
|
+
}
|
|
512
|
+
await Promise.allSettled([...inFlight]);
|
|
513
|
+
})();
|
|
514
|
+
return {
|
|
515
|
+
instructions: buildCodexFilesystemToolInstructions(tools, agent),
|
|
516
|
+
stop: async () => {
|
|
517
|
+
stopped = true;
|
|
518
|
+
await loop;
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
function buildCodexFilesystemToolWrapperScript(args) {
|
|
523
|
+
return `#!/usr/bin/env node
|
|
524
|
+
const fs = require('fs');
|
|
525
|
+
const path = require('path');
|
|
526
|
+
|
|
527
|
+
const toolName = ${JSON.stringify(args.toolName)};
|
|
528
|
+
const workspaceDir = ${JSON.stringify(args.workspaceDir)};
|
|
529
|
+
const requestDir = ${JSON.stringify(args.requestDir)};
|
|
530
|
+
const responseDir = ${JSON.stringify(args.responseDir)};
|
|
531
|
+
|
|
532
|
+
function sleep(ms) {
|
|
533
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function readText(filePath) {
|
|
537
|
+
return fs.readFileSync(path.resolve(process.cwd(), filePath), 'utf8');
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function readArgumentsJson() {
|
|
541
|
+
const argsPath = process.argv[2];
|
|
542
|
+
const raw = argsPath ? readText(argsPath) : fs.readFileSync(0, 'utf8');
|
|
543
|
+
if (!raw.trim()) {
|
|
544
|
+
throw new Error('Expected a JSON arguments file path or JSON on stdin.');
|
|
545
|
+
}
|
|
546
|
+
const parsed = JSON.parse(raw);
|
|
547
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
548
|
+
throw new Error('Tool arguments must be a JSON object.');
|
|
549
|
+
}
|
|
550
|
+
if (parsed.candidateLayeredDocumentPath && !parsed.candidateLayeredDocumentJson) {
|
|
551
|
+
parsed.candidateLayeredDocumentJson = readText(parsed.candidateLayeredDocumentPath);
|
|
552
|
+
delete parsed.candidateLayeredDocumentPath;
|
|
553
|
+
}
|
|
554
|
+
if (parsed.repairedLayeredDocumentPath && !parsed.repairedLayeredDocumentJson) {
|
|
555
|
+
parsed.repairedLayeredDocumentJson = readText(parsed.repairedLayeredDocumentPath);
|
|
556
|
+
delete parsed.repairedLayeredDocumentPath;
|
|
557
|
+
}
|
|
558
|
+
return { parsedArguments: parsed, argumentsJson: JSON.stringify(parsed) };
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function defaultOutputPath(toolName, id, key) {
|
|
562
|
+
const suffix = key === 'renderPngBase64' ? 'render.png' : key === 'diffPngBase64' ? 'diff.png' : key.replace(/Base64$/, '');
|
|
563
|
+
return path.join('tool-output', toolName + '-' + id + '-' + suffix);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function writeBase64Image(output, key, targetPath, toolName, id) {
|
|
567
|
+
if (!output || typeof output !== 'object' || typeof output[key] !== 'string') {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const outputPath = typeof targetPath === 'string' && targetPath.trim()
|
|
571
|
+
? targetPath
|
|
572
|
+
: defaultOutputPath(toolName, id, key);
|
|
573
|
+
const resolved = path.resolve(process.cwd(), outputPath);
|
|
574
|
+
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
575
|
+
fs.writeFileSync(resolved, Buffer.from(output[key], 'base64'));
|
|
576
|
+
output[key.replace(/Base64$/, 'Path')] = outputPath;
|
|
577
|
+
delete output[key];
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function materializeOutputFiles(rawOutput, parsedArguments, id) {
|
|
581
|
+
let output;
|
|
582
|
+
try {
|
|
583
|
+
output = JSON.parse(String(rawOutput || ''));
|
|
584
|
+
} catch {
|
|
585
|
+
return String(rawOutput || '');
|
|
586
|
+
}
|
|
587
|
+
writeBase64Image(output, 'renderPngBase64', parsedArguments.renderPngPath, toolName, id);
|
|
588
|
+
writeBase64Image(output, 'diffPngBase64', parsedArguments.diffPngPath, toolName, id);
|
|
589
|
+
return JSON.stringify(output, null, 2);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
async function main() {
|
|
593
|
+
process.chdir(workspaceDir);
|
|
594
|
+
fs.mkdirSync(requestDir, { recursive: true });
|
|
595
|
+
fs.mkdirSync(responseDir, { recursive: true });
|
|
596
|
+
const id = String(Date.now()) + '-' + String(process.pid) + '-' + Math.random().toString(16).slice(2);
|
|
597
|
+
const fileName = id + '.json';
|
|
598
|
+
const toolArguments = readArgumentsJson();
|
|
599
|
+
fs.writeFileSync(
|
|
600
|
+
path.join(requestDir, fileName),
|
|
601
|
+
JSON.stringify({ id, toolName, argumentsJson: toolArguments.argumentsJson }),
|
|
602
|
+
'utf8'
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
const responsePath = path.join(responseDir, fileName);
|
|
606
|
+
const deadline = Date.now() + 300000;
|
|
607
|
+
while (Date.now() < deadline) {
|
|
608
|
+
if (fs.existsSync(responsePath)) {
|
|
609
|
+
const response = JSON.parse(fs.readFileSync(responsePath, 'utf8'));
|
|
610
|
+
if (!response.ok) {
|
|
611
|
+
console.error(response.error || 'Tool failed.');
|
|
612
|
+
process.exit(1);
|
|
613
|
+
}
|
|
614
|
+
const output = materializeOutputFiles(response.output, toolArguments.parsedArguments, id);
|
|
615
|
+
process.stdout.write(output);
|
|
616
|
+
if (!output.endsWith('\\n')) process.stdout.write('\\n');
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
sleep(50);
|
|
620
|
+
}
|
|
621
|
+
console.error('Timed out waiting for tool result.');
|
|
622
|
+
process.exit(1);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
main().catch(error => {
|
|
626
|
+
console.error(error && error.message ? error.message : String(error));
|
|
627
|
+
process.exit(1);
|
|
628
|
+
});
|
|
629
|
+
`;
|
|
630
|
+
}
|
|
631
|
+
function normalizeCodexToolArguments(value, toolName) {
|
|
632
|
+
if (typeof value !== 'string') {
|
|
633
|
+
throw new Error(`Codex tool call ${toolName} must provide argumentsJson as a JSON string.`);
|
|
634
|
+
}
|
|
635
|
+
const parsed = JSON.parse(value);
|
|
636
|
+
if (!isRecord(parsed)) {
|
|
637
|
+
throw new Error(`Codex tool call ${toolName} argumentsJson must decode to a JSON object.`);
|
|
638
|
+
}
|
|
639
|
+
return JSON.stringify(parsed);
|
|
640
|
+
}
|
|
641
|
+
function parseCodexToolAction(content, tools, settings, terminalToolNames = []) {
|
|
642
|
+
const parsed = JSON.parse(content);
|
|
643
|
+
if (!isRecord(parsed)) {
|
|
644
|
+
throw new Error('Codex tool response must be a JSON object.');
|
|
645
|
+
}
|
|
646
|
+
const toolChoice = settings?.tool_choice;
|
|
647
|
+
const toolNames = new Set(getToolNames(tools));
|
|
648
|
+
const action = parsed.action;
|
|
649
|
+
if (action === 'final_response') {
|
|
650
|
+
if (terminalToolNames.length > 0) {
|
|
651
|
+
throw new Error(`Codex returned final_response when terminal tool ${terminalToolNames.join(' or ')} was required.`);
|
|
652
|
+
}
|
|
653
|
+
if (toolChoice === 'required') {
|
|
654
|
+
throw new Error('Codex returned final_response when tool_choice required a tool call.');
|
|
655
|
+
}
|
|
656
|
+
if (typeof toolChoice === 'object' && toolChoice?.type === 'function' && toolChoice.function?.name) {
|
|
657
|
+
throw new Error(`Codex returned final_response when ${toolChoice.function.name} was required.`);
|
|
658
|
+
}
|
|
659
|
+
return {
|
|
660
|
+
action: 'final_response',
|
|
661
|
+
content: typeof parsed.finalResponse === 'string' ? parsed.finalResponse : '',
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
if (action !== 'tool_calls') {
|
|
665
|
+
throw new Error(`Codex tool response action must be tool_calls or final_response; received ${String(action)}.`);
|
|
666
|
+
}
|
|
667
|
+
if (toolChoice === 'none') {
|
|
668
|
+
throw new Error('Codex returned tool_calls when tool_choice forbids tool use.');
|
|
669
|
+
}
|
|
670
|
+
if (!Array.isArray(parsed.toolCalls) || parsed.toolCalls.length === 0) {
|
|
671
|
+
throw new Error('Codex tool response must include at least one tool call.');
|
|
672
|
+
}
|
|
673
|
+
const requiredToolName = typeof toolChoice === 'object' && toolChoice?.type === 'function' && toolChoice.function?.name
|
|
674
|
+
? toolChoice.function.name
|
|
675
|
+
: undefined;
|
|
676
|
+
const toolCalls = parsed.toolCalls.map((rawCall, index) => {
|
|
677
|
+
if (!isRecord(rawCall)) {
|
|
678
|
+
throw new Error(`Codex tool call at index ${index} must be an object.`);
|
|
679
|
+
}
|
|
680
|
+
const name = rawCall.name;
|
|
681
|
+
if (typeof name !== 'string' || !toolNames.has(name)) {
|
|
682
|
+
throw new Error(`Codex requested unknown tool ${String(name)}.`);
|
|
683
|
+
}
|
|
684
|
+
if (requiredToolName && name !== requiredToolName) {
|
|
685
|
+
throw new Error(`Codex requested ${name}, but ${requiredToolName} was required.`);
|
|
686
|
+
}
|
|
687
|
+
const id = `codex_tool_${randomUUID()}`;
|
|
688
|
+
return {
|
|
689
|
+
id,
|
|
690
|
+
call_id: id,
|
|
691
|
+
type: 'function',
|
|
692
|
+
function: {
|
|
693
|
+
name,
|
|
694
|
+
arguments: normalizeCodexToolArguments(rawCall.argumentsJson, name),
|
|
695
|
+
},
|
|
696
|
+
};
|
|
697
|
+
});
|
|
698
|
+
return {
|
|
699
|
+
action: 'tool_calls',
|
|
700
|
+
toolCalls,
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
async function readOptionalUtf8File(filePath) {
|
|
704
|
+
try {
|
|
705
|
+
return await readFile(filePath, 'utf8');
|
|
706
|
+
}
|
|
707
|
+
catch (error) {
|
|
708
|
+
if (error.code === 'ENOENT') {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
throw error;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
235
714
|
function serializeCodexExecError(error) {
|
|
236
715
|
if (!(error instanceof Error))
|
|
237
716
|
return error;
|
|
@@ -355,30 +834,60 @@ async function runCodexExec(options) {
|
|
|
355
834
|
});
|
|
356
835
|
return { stdout, stderr, diagnostics };
|
|
357
836
|
}
|
|
358
|
-
function disabledFeatureArgs(
|
|
359
|
-
return CODEX_DISABLED_FEATURES.filter(feature =>
|
|
837
|
+
function disabledFeatureArgs(options = {}) {
|
|
838
|
+
return CODEX_DISABLED_FEATURES.filter(feature => {
|
|
839
|
+
if (options.allowImageGeneration && feature === 'image_generation')
|
|
840
|
+
return false;
|
|
841
|
+
if (options.allowShellTool && feature === 'shell_tool')
|
|
842
|
+
return false;
|
|
843
|
+
return true;
|
|
844
|
+
}).flatMap(feature => ['--disable', feature]);
|
|
360
845
|
}
|
|
361
|
-
async function executeCodexRequest(messages, model, agent, requestId) {
|
|
362
|
-
if (agent.
|
|
363
|
-
throw new Error('Codex provider v1 does not support
|
|
846
|
+
async function executeCodexRequest(messages, model, agent, requestId, options = {}) {
|
|
847
|
+
if (agent.params || agent.processParams) {
|
|
848
|
+
throw new Error('Codex provider v1 does not support params requests.');
|
|
364
849
|
}
|
|
365
850
|
const settings = agent.modelSettings;
|
|
851
|
+
const codexSettings = getCodexProviderSettings(settings);
|
|
852
|
+
const requestJsonSchema = options.jsonSchema ?? settings?.json_schema;
|
|
366
853
|
const { model: codexModel, effort } = resolveCodexModel(model, settings);
|
|
367
854
|
const codexHome = resolveCodexHome(settings);
|
|
368
|
-
const cwd = agent.cwd || process.cwd();
|
|
369
855
|
const tempDir = await mkdtemp(path.join(tmpdir(), 'ensemble-codex-'));
|
|
856
|
+
const cwd = options.useTempCwd ? tempDir : agent.cwd || process.cwd();
|
|
370
857
|
const imageWriter = new CodexImageAttachmentWriter(tempDir, cwd);
|
|
371
|
-
|
|
858
|
+
let filesystemToolBroker = null;
|
|
859
|
+
const workspaceFileNames = await writeCodexWorkspaceFiles(tempDir, codexSettings?.codex_workspace_files);
|
|
860
|
+
if (options.filesystemTools?.length) {
|
|
861
|
+
filesystemToolBroker = await startCodexFilesystemToolBroker(tempDir, options.filesystemTools, agent);
|
|
862
|
+
}
|
|
863
|
+
const extraInstructions = [
|
|
864
|
+
buildCodexWorkspaceFileInstructions(workspaceFileNames, codexSettings?.codex_workspace_instructions),
|
|
865
|
+
filesystemToolBroker?.instructions,
|
|
866
|
+
options.extraInstructions,
|
|
867
|
+
]
|
|
868
|
+
.map(value => value?.trim() ?? '')
|
|
869
|
+
.filter(Boolean)
|
|
870
|
+
.join('\n\n');
|
|
871
|
+
const requestAgent = extraInstructions
|
|
872
|
+
? {
|
|
873
|
+
...agent,
|
|
874
|
+
instructions: [agent.instructions, extraInstructions]
|
|
875
|
+
.map(value => value?.trim() ?? '')
|
|
876
|
+
.filter(Boolean)
|
|
877
|
+
.join('\n\n'),
|
|
878
|
+
}
|
|
879
|
+
: agent;
|
|
880
|
+
const { instructions, prompt, images } = await buildCodexInput(messages, requestAgent, imageWriter);
|
|
372
881
|
const hasInstructions = instructions.trim().length > 0;
|
|
373
882
|
const instructionsPath = path.join(tempDir, 'instructions.md');
|
|
374
883
|
const lastMessagePath = path.join(tempDir, 'last-message.json');
|
|
375
|
-
const schemaPath =
|
|
884
|
+
const schemaPath = requestJsonSchema?.schema ? path.join(tempDir, 'schema.json') : undefined;
|
|
376
885
|
try {
|
|
377
886
|
if (hasInstructions) {
|
|
378
887
|
await writeFile(instructionsPath, instructions, 'utf8');
|
|
379
888
|
}
|
|
380
889
|
if (schemaPath) {
|
|
381
|
-
await writeFile(schemaPath, JSON.stringify(
|
|
890
|
+
await writeFile(schemaPath, JSON.stringify(requestJsonSchema.schema, null, 2), 'utf8');
|
|
382
891
|
}
|
|
383
892
|
const commandArgs = [
|
|
384
893
|
'exec',
|
|
@@ -387,7 +896,7 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
387
896
|
'--ignore-user-config',
|
|
388
897
|
'--ignore-rules',
|
|
389
898
|
'--skip-git-repo-check',
|
|
390
|
-
...disabledFeatureArgs(),
|
|
899
|
+
...disabledFeatureArgs({ allowShellTool: options.allowShellTool }),
|
|
391
900
|
'-m',
|
|
392
901
|
codexModel,
|
|
393
902
|
'-c',
|
|
@@ -395,19 +904,21 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
395
904
|
...(hasInstructions ? ['-c', `model_instructions_file=${JSON.stringify(instructionsPath)}`] : []),
|
|
396
905
|
...(schemaPath ? ['--output-schema', schemaPath] : []),
|
|
397
906
|
...(images.length > 0 ? ['--image', images.join(',')] : []),
|
|
907
|
+
...(options.allowShellTool ? ['--sandbox', 'workspace-write', '--add-dir', tempDir] : []),
|
|
398
908
|
'--output-last-message',
|
|
399
909
|
lastMessagePath,
|
|
400
910
|
'--cd',
|
|
401
911
|
cwd,
|
|
402
912
|
'-',
|
|
403
913
|
];
|
|
404
|
-
const loggedRequestId = log_llm_request(agent.agent_id || 'default', 'codex',
|
|
914
|
+
const loggedRequestId = log_llm_request(agent.agent_id || 'default', 'codex', model, {
|
|
405
915
|
command: 'codex',
|
|
406
916
|
args: commandArgs,
|
|
407
917
|
cwd,
|
|
408
918
|
prompt,
|
|
409
919
|
images,
|
|
410
|
-
|
|
920
|
+
codex_model: codexModel,
|
|
921
|
+
schema: requestJsonSchema?.schema,
|
|
411
922
|
}, new Date(), requestId, agent.tags);
|
|
412
923
|
let codexExecDiagnostics;
|
|
413
924
|
try {
|
|
@@ -422,7 +933,9 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
422
933
|
abortSignal: agent.abortSignal,
|
|
423
934
|
});
|
|
424
935
|
codexExecDiagnostics = codexExecResult.diagnostics;
|
|
425
|
-
const usage = codexUsageFromJsonl(codexExecResult.stdout,
|
|
936
|
+
const usage = codexUsageFromJsonl(codexExecResult.stdout, model, loggedRequestId, {
|
|
937
|
+
codex_cli_model: codexModel,
|
|
938
|
+
});
|
|
426
939
|
if (usage)
|
|
427
940
|
costTracker.addUsage(usage);
|
|
428
941
|
}
|
|
@@ -440,7 +953,8 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
440
953
|
catch (error) {
|
|
441
954
|
throw new Error(`Codex CLI did not write the expected --output-last-message file: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
442
955
|
}
|
|
443
|
-
const
|
|
956
|
+
const finalFiles = await readCodexWorkspaceFinalFiles(tempDir, codexSettings?.codex_workspace_final_files);
|
|
957
|
+
const content = appendCodexWorkspaceFinalFiles(normalizeLastMessageContent(rawLastMessage, Boolean(schemaPath)), finalFiles);
|
|
444
958
|
log_llm_response(loggedRequestId, { content, codex_exec: codexExecDiagnostics });
|
|
445
959
|
return content;
|
|
446
960
|
}
|
|
@@ -449,9 +963,81 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
449
963
|
throw error;
|
|
450
964
|
}
|
|
451
965
|
finally {
|
|
966
|
+
await filesystemToolBroker?.stop();
|
|
452
967
|
await rm(tempDir, { recursive: true, force: true });
|
|
453
968
|
}
|
|
454
969
|
}
|
|
970
|
+
async function* executeCodexToolRequest(messages, model, agent, tools, requestId) {
|
|
971
|
+
if (agent.modelSettings?.json_schema?.schema) {
|
|
972
|
+
throw new Error('Codex provider cannot combine simulated tool calls with a caller-supplied json_schema.');
|
|
973
|
+
}
|
|
974
|
+
const content = await executeCodexRequest(messages, model, agent, requestId, {
|
|
975
|
+
extraInstructions: buildCodexToolInstructions(tools, agent),
|
|
976
|
+
jsonSchema: createCodexToolOutputSchema(tools, agent),
|
|
977
|
+
});
|
|
978
|
+
const action = parseCodexToolAction(content, tools, agent.modelSettings, getAvailableTerminalToolNames(tools, agent));
|
|
979
|
+
if (action.action === 'final_response') {
|
|
980
|
+
const messageId = `codex-${Date.now()}`;
|
|
981
|
+
yield {
|
|
982
|
+
type: 'message_complete',
|
|
983
|
+
content: action.content,
|
|
984
|
+
message_id: messageId,
|
|
985
|
+
};
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
for (const toolCall of action.toolCalls) {
|
|
989
|
+
yield {
|
|
990
|
+
type: 'tool_start',
|
|
991
|
+
tool_call: toolCall,
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
async function* executeCodexFilesystemToolRequest(messages, model, agent, tools, requestId) {
|
|
996
|
+
if (agent.modelSettings?.json_schema?.schema) {
|
|
997
|
+
throw new Error('Codex provider cannot combine filesystem tool mode with a caller-supplied json_schema.');
|
|
998
|
+
}
|
|
999
|
+
const content = await executeCodexRequest(messages, model, agent, requestId, {
|
|
1000
|
+
allowShellTool: true,
|
|
1001
|
+
filesystemTools: tools,
|
|
1002
|
+
useTempCwd: true,
|
|
1003
|
+
});
|
|
1004
|
+
const messageId = `codex-${Date.now()}`;
|
|
1005
|
+
if (content) {
|
|
1006
|
+
yield {
|
|
1007
|
+
type: 'message_delta',
|
|
1008
|
+
content,
|
|
1009
|
+
message_id: messageId,
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
yield {
|
|
1013
|
+
type: 'message_complete',
|
|
1014
|
+
content,
|
|
1015
|
+
message_id: messageId,
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
async function* executeCodexWorkspaceFileRequest(messages, model, agent, tools, requestId) {
|
|
1019
|
+
if (agent.modelSettings?.json_schema?.schema) {
|
|
1020
|
+
throw new Error('Codex provider cannot combine workspace file mode with a caller-supplied json_schema.');
|
|
1021
|
+
}
|
|
1022
|
+
const content = await executeCodexRequest(messages, model, agent, requestId, {
|
|
1023
|
+
allowShellTool: true,
|
|
1024
|
+
filesystemTools: tools,
|
|
1025
|
+
useTempCwd: true,
|
|
1026
|
+
});
|
|
1027
|
+
const messageId = `codex-${Date.now()}`;
|
|
1028
|
+
if (content) {
|
|
1029
|
+
yield {
|
|
1030
|
+
type: 'message_delta',
|
|
1031
|
+
content,
|
|
1032
|
+
message_id: messageId,
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
yield {
|
|
1036
|
+
type: 'message_complete',
|
|
1037
|
+
content,
|
|
1038
|
+
message_id: messageId,
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
455
1041
|
function buildCodexImagePrompt(prompt, opts = {}) {
|
|
456
1042
|
const count = opts.n && opts.n > 0 ? Math.floor(opts.n) : 1;
|
|
457
1043
|
const details = [
|
|
@@ -534,7 +1120,7 @@ async function executeCodexImageGeneration(prompt, model, agent, opts = {}) {
|
|
|
534
1120
|
'--skip-git-repo-check',
|
|
535
1121
|
'--enable',
|
|
536
1122
|
'image_generation',
|
|
537
|
-
...disabledFeatureArgs(true),
|
|
1123
|
+
...disabledFeatureArgs({ allowImageGeneration: true }),
|
|
538
1124
|
...(promptModelAttempt
|
|
539
1125
|
? [
|
|
540
1126
|
'-m',
|
|
@@ -583,8 +1169,11 @@ async function executeCodexImageGeneration(prompt, model, agent, opts = {}) {
|
|
|
583
1169
|
});
|
|
584
1170
|
throw error;
|
|
585
1171
|
}
|
|
586
|
-
const rawLastMessage = await
|
|
587
|
-
const
|
|
1172
|
+
const rawLastMessage = await readOptionalUtf8File(lastMessagePath);
|
|
1173
|
+
const lastMessageContent = rawLastMessage ?? '';
|
|
1174
|
+
const responseImagePaths = rawLastMessage
|
|
1175
|
+
? await extractExistingCodexImagePaths(rawLastMessage, outputDir)
|
|
1176
|
+
: [];
|
|
588
1177
|
const outputImagePaths = await newestFirst(await listCodexOutputImages(outputDir));
|
|
589
1178
|
const generatedImagePaths = await newestFirst(await listCodexGeneratedImages(isolatedCodexHome));
|
|
590
1179
|
const selectedImagePaths = [];
|
|
@@ -596,8 +1185,9 @@ async function executeCodexImageGeneration(prompt, model, agent, opts = {}) {
|
|
|
596
1185
|
break;
|
|
597
1186
|
}
|
|
598
1187
|
if (selectedImagePaths.length < expectedImageCount) {
|
|
599
|
-
const lastMessage =
|
|
600
|
-
|
|
1188
|
+
const lastMessage = lastMessageContent.trim();
|
|
1189
|
+
const lastMessageNote = rawLastMessage === null ? ' Codex CLI did not write --output-last-message.' : '';
|
|
1190
|
+
throw new Error(`Codex image generation resolved ${selectedImagePaths.length} image artifact${selectedImagePaths.length === 1 ? '' : 's'}, expected ${expectedImageCount}.${lastMessageNote}${lastMessage ? ` Last message: ${lastMessage}` : ''}`);
|
|
601
1191
|
}
|
|
602
1192
|
const generatedImages = await readCodexImageFiles(selectedImagePaths);
|
|
603
1193
|
log_llm_response(loggedRequestId, {
|
|
@@ -606,7 +1196,8 @@ async function executeCodexImageGeneration(prompt, model, agent, opts = {}) {
|
|
|
606
1196
|
response_image_paths: responseImagePaths,
|
|
607
1197
|
output_image_paths: outputImagePaths,
|
|
608
1198
|
codex_home_image_paths: generatedImagePaths,
|
|
609
|
-
last_message:
|
|
1199
|
+
last_message: lastMessageContent.trim(),
|
|
1200
|
+
last_message_missing: rawLastMessage === null,
|
|
610
1201
|
codex_exec: codexExecDiagnostics,
|
|
611
1202
|
});
|
|
612
1203
|
costTracker.addUsage({
|
|
@@ -646,6 +1237,21 @@ export class CodexProvider extends BaseModelProvider {
|
|
|
646
1237
|
}
|
|
647
1238
|
async *createResponseStream(messages, model, agent, requestId) {
|
|
648
1239
|
try {
|
|
1240
|
+
const { getToolsFromAgent } = await import('../utils/agent.js');
|
|
1241
|
+
const tools = agent ? await getToolsFromAgent(agent) : [];
|
|
1242
|
+
if (tools.length > 0) {
|
|
1243
|
+
const codexToolTransport = getCodexProviderSettings(agent.modelSettings)?.codex_tool_transport;
|
|
1244
|
+
if (codexToolTransport === 'workspace-files') {
|
|
1245
|
+
yield* executeCodexWorkspaceFileRequest(messages, model, agent, tools, requestId);
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
if (codexToolTransport === 'filesystem') {
|
|
1249
|
+
yield* executeCodexFilesystemToolRequest(messages, model, agent, tools, requestId);
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
yield* executeCodexToolRequest(messages, model, agent, tools, requestId);
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
649
1255
|
const content = await executeCodexRequest(messages, model, agent, requestId);
|
|
650
1256
|
const messageId = `codex-${Date.now()}`;
|
|
651
1257
|
if (content) {
|