@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,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -6,9 +39,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
39
|
exports.codexProvider = exports.CodexProvider = void 0;
|
|
7
40
|
exports.resolveCodexModel = resolveCodexModel;
|
|
8
41
|
const child_process_1 = require("child_process");
|
|
42
|
+
const crypto_1 = require("crypto");
|
|
9
43
|
const promises_1 = require("fs/promises");
|
|
10
44
|
const os_1 = require("os");
|
|
11
45
|
const path_1 = __importDefault(require("path"));
|
|
46
|
+
const promises_2 = require("timers/promises");
|
|
12
47
|
const base_provider_js_1 = require("./base_provider.cjs");
|
|
13
48
|
const cost_tracker_js_1 = require("../utils/cost_tracker.cjs");
|
|
14
49
|
const llm_logger_js_1 = require("../utils/llm_logger.cjs");
|
|
@@ -190,7 +225,7 @@ function summarizeCodexExecStream(value) {
|
|
|
190
225
|
truncated: true,
|
|
191
226
|
};
|
|
192
227
|
}
|
|
193
|
-
function codexUsageFromJsonl(stdout, model, requestId) {
|
|
228
|
+
function codexUsageFromJsonl(stdout, model, requestId, metadata) {
|
|
194
229
|
let latestUsage;
|
|
195
230
|
for (const line of stdout.split(/\r?\n/)) {
|
|
196
231
|
const trimmed = line.trim();
|
|
@@ -228,6 +263,7 @@ function codexUsageFromJsonl(stdout, model, requestId) {
|
|
|
228
263
|
cached_tokens: latestUsage.cached_input_tokens,
|
|
229
264
|
request_id: requestId,
|
|
230
265
|
metadata: {
|
|
266
|
+
...metadata,
|
|
231
267
|
source: 'codex_cli_json',
|
|
232
268
|
reasoning_output_tokens: latestUsage.reasoning_output_tokens ?? 0,
|
|
233
269
|
},
|
|
@@ -239,6 +275,482 @@ function finiteNumber(value) {
|
|
|
239
275
|
function isRecord(value) {
|
|
240
276
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
241
277
|
}
|
|
278
|
+
function getCodexProviderSettings(settings) {
|
|
279
|
+
return settings;
|
|
280
|
+
}
|
|
281
|
+
function compactJson(value) {
|
|
282
|
+
return JSON.stringify(value);
|
|
283
|
+
}
|
|
284
|
+
function getToolNames(tools) {
|
|
285
|
+
return tools.map(tool => tool.definition.function.name);
|
|
286
|
+
}
|
|
287
|
+
function getTerminalToolNameSet(agent) {
|
|
288
|
+
return new Set((agent.terminalToolNames ?? []).filter((name) => typeof name === 'string' && name.trim().length > 0));
|
|
289
|
+
}
|
|
290
|
+
function getAvailableTerminalToolNames(tools, agent) {
|
|
291
|
+
const terminalNames = getTerminalToolNameSet(agent);
|
|
292
|
+
if (terminalNames.size === 0)
|
|
293
|
+
return [];
|
|
294
|
+
return getToolNames(tools).filter(name => terminalNames.has(name));
|
|
295
|
+
}
|
|
296
|
+
function createCodexToolOutputSchema(tools, agent) {
|
|
297
|
+
const requiresTerminalTool = getAvailableTerminalToolNames(tools, agent).length > 0;
|
|
298
|
+
return {
|
|
299
|
+
name: 'codex_tool_action',
|
|
300
|
+
type: 'json_schema',
|
|
301
|
+
description: 'A simulated Ensemble tool action for Codex CLI provider requests.',
|
|
302
|
+
schema: {
|
|
303
|
+
type: 'object',
|
|
304
|
+
additionalProperties: false,
|
|
305
|
+
properties: {
|
|
306
|
+
action: {
|
|
307
|
+
type: 'string',
|
|
308
|
+
enum: requiresTerminalTool ? ['tool_calls'] : ['tool_calls', 'final_response'],
|
|
309
|
+
description: requiresTerminalTool
|
|
310
|
+
? 'Use tool_calls. This request is only complete after calling a terminal tool.'
|
|
311
|
+
: 'Use tool_calls when requesting Ensemble tool execution; use final_response only when no tool is needed.',
|
|
312
|
+
},
|
|
313
|
+
toolCalls: {
|
|
314
|
+
type: 'array',
|
|
315
|
+
description: 'Tool calls to execute in parallel for this turn. Empty when action is final_response.',
|
|
316
|
+
items: {
|
|
317
|
+
type: 'object',
|
|
318
|
+
additionalProperties: false,
|
|
319
|
+
properties: {
|
|
320
|
+
name: {
|
|
321
|
+
type: 'string',
|
|
322
|
+
enum: getToolNames(tools),
|
|
323
|
+
},
|
|
324
|
+
argumentsJson: {
|
|
325
|
+
type: 'string',
|
|
326
|
+
description: 'A compact JSON string containing the complete arguments object for the named tool.',
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
required: ['name', 'argumentsJson'],
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
finalResponse: {
|
|
333
|
+
type: 'string',
|
|
334
|
+
description: 'Final assistant response. Use an empty string when action is tool_calls.',
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
required: ['action', 'toolCalls', 'finalResponse'],
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function describeCodexToolChoice(settings) {
|
|
342
|
+
const toolChoice = settings?.tool_choice;
|
|
343
|
+
if (toolChoice === 'required') {
|
|
344
|
+
return 'This turn requires at least one tool call.';
|
|
345
|
+
}
|
|
346
|
+
if (toolChoice === 'none') {
|
|
347
|
+
return 'This turn forbids tool calls; return final_response.';
|
|
348
|
+
}
|
|
349
|
+
if (typeof toolChoice === 'object' && toolChoice?.type === 'function' && toolChoice.function?.name) {
|
|
350
|
+
return `This turn requires the ${toolChoice.function.name} tool.`;
|
|
351
|
+
}
|
|
352
|
+
return 'Use tool_calls when a tool result is needed; otherwise use final_response.';
|
|
353
|
+
}
|
|
354
|
+
function buildCodexToolInstructions(tools, agent) {
|
|
355
|
+
const settings = agent.modelSettings;
|
|
356
|
+
const terminalToolNames = getAvailableTerminalToolNames(tools, agent);
|
|
357
|
+
const toolDescriptions = tools
|
|
358
|
+
.map(tool => {
|
|
359
|
+
const fn = tool.definition.function;
|
|
360
|
+
return [
|
|
361
|
+
`Tool: ${fn.name}`,
|
|
362
|
+
`Description: ${fn.description}`,
|
|
363
|
+
`Parameters JSON schema: ${compactJson(fn.parameters)}`,
|
|
364
|
+
].join('\n');
|
|
365
|
+
})
|
|
366
|
+
.join('\n\n');
|
|
367
|
+
return [
|
|
368
|
+
'You are using Ensemble simulated tool mode.',
|
|
369
|
+
'You cannot execute these tools yourself. Instead, respond only with JSON matching the provided output schema.',
|
|
370
|
+
describeCodexToolChoice(settings),
|
|
371
|
+
'For action "tool_calls", include one or more entries in toolCalls and set finalResponse to an empty string.',
|
|
372
|
+
terminalToolNames.length > 0
|
|
373
|
+
? `This request is not complete until you call one of these terminal tools: ${terminalToolNames.join(', ')}. Do not use final_response.`
|
|
374
|
+
: 'For action "final_response", set toolCalls to an empty array and put the final answer in finalResponse.',
|
|
375
|
+
'Use exact tool names.',
|
|
376
|
+
'Set argumentsJson to a compact JSON string containing the complete arguments object for the named tool.',
|
|
377
|
+
'For example, the arguments object {"label":"ok"} must be encoded as "{\\"label\\":\\"ok\\"}".',
|
|
378
|
+
'',
|
|
379
|
+
'Available tools:',
|
|
380
|
+
toolDescriptions,
|
|
381
|
+
].join('\n');
|
|
382
|
+
}
|
|
383
|
+
function validateCodexWorkspaceRelativePath(filePath) {
|
|
384
|
+
const normalized = filePath.replace(/\\/g, '/').replace(/^\.\/+/, '');
|
|
385
|
+
if (!normalized ||
|
|
386
|
+
path_1.default.isAbsolute(normalized) ||
|
|
387
|
+
normalized.split('/').some(part => part === '..' || part === '')) {
|
|
388
|
+
throw new Error(`Invalid Codex workspace file path: ${filePath}`);
|
|
389
|
+
}
|
|
390
|
+
return normalized;
|
|
391
|
+
}
|
|
392
|
+
async function writeCodexWorkspaceFiles(tempDir, files) {
|
|
393
|
+
const written = [];
|
|
394
|
+
for (const [rawPath, content] of Object.entries(files ?? {})) {
|
|
395
|
+
const relativePath = validateCodexWorkspaceRelativePath(rawPath);
|
|
396
|
+
const targetPath = path_1.default.join(tempDir, relativePath);
|
|
397
|
+
await (0, promises_1.mkdir)(path_1.default.dirname(targetPath), { recursive: true });
|
|
398
|
+
const dataUrlMatch = /^data:[^;,]+;base64,([\s\S]*)$/i.exec(content);
|
|
399
|
+
if (dataUrlMatch) {
|
|
400
|
+
await (0, promises_1.writeFile)(targetPath, Buffer.from(dataUrlMatch[1] ?? '', 'base64'));
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
await (0, promises_1.writeFile)(targetPath, content, 'utf8');
|
|
404
|
+
}
|
|
405
|
+
written.push(relativePath);
|
|
406
|
+
}
|
|
407
|
+
return written.sort();
|
|
408
|
+
}
|
|
409
|
+
function normalizeCodexWorkspaceFinalFiles(files) {
|
|
410
|
+
return [...new Set((files ?? []).map(validateCodexWorkspaceRelativePath))].sort();
|
|
411
|
+
}
|
|
412
|
+
async function readCodexWorkspaceFinalFiles(tempDir, files) {
|
|
413
|
+
const output = {};
|
|
414
|
+
for (const relativePath of normalizeCodexWorkspaceFinalFiles(files)) {
|
|
415
|
+
const filePath = path_1.default.join(tempDir, relativePath);
|
|
416
|
+
output[relativePath] = await (0, promises_1.readFile)(filePath, 'utf8');
|
|
417
|
+
}
|
|
418
|
+
return output;
|
|
419
|
+
}
|
|
420
|
+
function appendCodexWorkspaceFinalFiles(content, files) {
|
|
421
|
+
const entries = Object.entries(files);
|
|
422
|
+
if (entries.length === 0)
|
|
423
|
+
return content;
|
|
424
|
+
return [
|
|
425
|
+
content,
|
|
426
|
+
'',
|
|
427
|
+
'<codex_workspace_files_json>',
|
|
428
|
+
JSON.stringify(Object.fromEntries(entries), null, 2),
|
|
429
|
+
'</codex_workspace_files_json>',
|
|
430
|
+
].join('\n');
|
|
431
|
+
}
|
|
432
|
+
function buildCodexWorkspaceFileInstructions(files, extra) {
|
|
433
|
+
if (files.length === 0 && !extra?.trim())
|
|
434
|
+
return '';
|
|
435
|
+
return [
|
|
436
|
+
'A disposable Codex workspace has been prepared for this request.',
|
|
437
|
+
files.length > 0
|
|
438
|
+
? `Workspace files available in the current directory:\n${files.map(file => `- ${file}`).join('\n')}`
|
|
439
|
+
: undefined,
|
|
440
|
+
extra?.trim() || undefined,
|
|
441
|
+
]
|
|
442
|
+
.filter(Boolean)
|
|
443
|
+
.join('\n');
|
|
444
|
+
}
|
|
445
|
+
function shellIdentifier(value) {
|
|
446
|
+
if (!/^[A-Za-z0-9_.:-]+$/.test(value)) {
|
|
447
|
+
throw new Error(`Codex filesystem tool name is not shell-safe: ${value}`);
|
|
448
|
+
}
|
|
449
|
+
return value;
|
|
450
|
+
}
|
|
451
|
+
function buildCodexFilesystemToolInstructions(tools, agent) {
|
|
452
|
+
const terminalToolNames = getAvailableTerminalToolNames(tools, agent);
|
|
453
|
+
const toolDescriptions = tools
|
|
454
|
+
.map(tool => {
|
|
455
|
+
const fn = tool.definition.function;
|
|
456
|
+
return [
|
|
457
|
+
`Command: ./tools/${fn.name} <arguments-json-file>`,
|
|
458
|
+
`Description: ${fn.description}`,
|
|
459
|
+
`Arguments JSON schema: ${compactJson(fn.parameters)}`,
|
|
460
|
+
].join('\n');
|
|
461
|
+
})
|
|
462
|
+
.join('\n\n');
|
|
463
|
+
return [
|
|
464
|
+
'You are using Codex filesystem tool mode.',
|
|
465
|
+
'Work in the current directory. Read the provided JSON files, write candidate JSON files, and run the executable commands in ./tools.',
|
|
466
|
+
'Each ./tools command takes one path to a JSON arguments file. The command prints the real tool result to stdout.',
|
|
467
|
+
'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.',
|
|
468
|
+
terminalToolNames.length > 0
|
|
469
|
+
? `This request is not complete until you run one of these terminal tool commands: ${terminalToolNames.map(name => `./tools/${name}`).join(', ')}.`
|
|
470
|
+
: undefined,
|
|
471
|
+
terminalToolNames.length > 0
|
|
472
|
+
? 'After the terminal tool command succeeds, respond with a brief completion note. Do not invent tool outputs; use the command output.'
|
|
473
|
+
: 'Run the commands you need, then respond with a brief completion note. Do not invent command outputs; use stdout from the commands.',
|
|
474
|
+
'',
|
|
475
|
+
'Available filesystem tools:',
|
|
476
|
+
toolDescriptions,
|
|
477
|
+
]
|
|
478
|
+
.filter(Boolean)
|
|
479
|
+
.join('\n');
|
|
480
|
+
}
|
|
481
|
+
async function startCodexFilesystemToolBroker(tempDir, tools, agent) {
|
|
482
|
+
const toolsDir = path_1.default.join(tempDir, 'tools');
|
|
483
|
+
const requestDir = path_1.default.join(tempDir, '.codex-tool-requests');
|
|
484
|
+
const responseDir = path_1.default.join(tempDir, '.codex-tool-responses');
|
|
485
|
+
await Promise.all([
|
|
486
|
+
(0, promises_1.mkdir)(toolsDir, { recursive: true }),
|
|
487
|
+
(0, promises_1.mkdir)(requestDir, { recursive: true }),
|
|
488
|
+
(0, promises_1.mkdir)(responseDir, { recursive: true }),
|
|
489
|
+
]);
|
|
490
|
+
for (const tool of tools) {
|
|
491
|
+
const toolName = shellIdentifier(tool.definition.function.name);
|
|
492
|
+
const toolPath = path_1.default.join(toolsDir, toolName);
|
|
493
|
+
await (0, promises_1.writeFile)(toolPath, buildCodexFilesystemToolWrapperScript({
|
|
494
|
+
toolName,
|
|
495
|
+
workspaceDir: tempDir,
|
|
496
|
+
requestDir,
|
|
497
|
+
responseDir,
|
|
498
|
+
}), 'utf8');
|
|
499
|
+
await (0, promises_1.chmod)(toolPath, 0o755);
|
|
500
|
+
}
|
|
501
|
+
const toolByName = new Map(tools.map(tool => [tool.definition.function.name, tool]));
|
|
502
|
+
const seen = new Set();
|
|
503
|
+
const inFlight = new Set();
|
|
504
|
+
let stopped = false;
|
|
505
|
+
const serviceRequest = async (fileName) => {
|
|
506
|
+
const requestPath = path_1.default.join(requestDir, fileName);
|
|
507
|
+
let request;
|
|
508
|
+
try {
|
|
509
|
+
request = JSON.parse(await (0, promises_1.readFile)(requestPath, 'utf8'));
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
await (0, promises_1.writeFile)(path_1.default.join(responseDir, fileName), JSON.stringify({
|
|
513
|
+
ok: false,
|
|
514
|
+
error: `Could not read Codex filesystem tool request: ${error instanceof Error ? error.message : String(error)}`,
|
|
515
|
+
}), 'utf8');
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const toolName = typeof request.toolName === 'string' ? request.toolName : '';
|
|
519
|
+
const tool = toolByName.get(toolName);
|
|
520
|
+
if (!tool) {
|
|
521
|
+
await (0, promises_1.writeFile)(path_1.default.join(responseDir, fileName), JSON.stringify({ ok: false, error: `Unknown Codex filesystem tool: ${toolName || '<missing>'}` }), 'utf8');
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
try {
|
|
525
|
+
const argumentsJson = typeof request.argumentsJson === 'string' ? request.argumentsJson : '{}';
|
|
526
|
+
const parsedArguments = JSON.parse(argumentsJson);
|
|
527
|
+
const result = await tool.function(parsedArguments);
|
|
528
|
+
const output = typeof result === 'string' ? result : JSON.stringify(result);
|
|
529
|
+
await (0, promises_1.writeFile)(path_1.default.join(responseDir, fileName), JSON.stringify({ ok: true, output }), 'utf8');
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
await (0, promises_1.writeFile)(path_1.default.join(responseDir, fileName), JSON.stringify({
|
|
533
|
+
ok: false,
|
|
534
|
+
error: error instanceof Error ? error.message : String(error),
|
|
535
|
+
}), 'utf8');
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
const loop = (async () => {
|
|
539
|
+
while (!stopped) {
|
|
540
|
+
const entries = await (0, promises_1.readdir)(requestDir).catch(() => []);
|
|
541
|
+
for (const fileName of entries) {
|
|
542
|
+
if (!fileName.endsWith('.json') || seen.has(fileName))
|
|
543
|
+
continue;
|
|
544
|
+
seen.add(fileName);
|
|
545
|
+
const task = serviceRequest(fileName).finally(() => {
|
|
546
|
+
inFlight.delete(task);
|
|
547
|
+
});
|
|
548
|
+
inFlight.add(task);
|
|
549
|
+
}
|
|
550
|
+
await (0, promises_2.setTimeout)(50);
|
|
551
|
+
}
|
|
552
|
+
await Promise.allSettled([...inFlight]);
|
|
553
|
+
})();
|
|
554
|
+
return {
|
|
555
|
+
instructions: buildCodexFilesystemToolInstructions(tools, agent),
|
|
556
|
+
stop: async () => {
|
|
557
|
+
stopped = true;
|
|
558
|
+
await loop;
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
function buildCodexFilesystemToolWrapperScript(args) {
|
|
563
|
+
return `#!/usr/bin/env node
|
|
564
|
+
const fs = require('fs');
|
|
565
|
+
const path = require('path');
|
|
566
|
+
|
|
567
|
+
const toolName = ${JSON.stringify(args.toolName)};
|
|
568
|
+
const workspaceDir = ${JSON.stringify(args.workspaceDir)};
|
|
569
|
+
const requestDir = ${JSON.stringify(args.requestDir)};
|
|
570
|
+
const responseDir = ${JSON.stringify(args.responseDir)};
|
|
571
|
+
|
|
572
|
+
function sleep(ms) {
|
|
573
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function readText(filePath) {
|
|
577
|
+
return fs.readFileSync(path.resolve(process.cwd(), filePath), 'utf8');
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function readArgumentsJson() {
|
|
581
|
+
const argsPath = process.argv[2];
|
|
582
|
+
const raw = argsPath ? readText(argsPath) : fs.readFileSync(0, 'utf8');
|
|
583
|
+
if (!raw.trim()) {
|
|
584
|
+
throw new Error('Expected a JSON arguments file path or JSON on stdin.');
|
|
585
|
+
}
|
|
586
|
+
const parsed = JSON.parse(raw);
|
|
587
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
588
|
+
throw new Error('Tool arguments must be a JSON object.');
|
|
589
|
+
}
|
|
590
|
+
if (parsed.candidateLayeredDocumentPath && !parsed.candidateLayeredDocumentJson) {
|
|
591
|
+
parsed.candidateLayeredDocumentJson = readText(parsed.candidateLayeredDocumentPath);
|
|
592
|
+
delete parsed.candidateLayeredDocumentPath;
|
|
593
|
+
}
|
|
594
|
+
if (parsed.repairedLayeredDocumentPath && !parsed.repairedLayeredDocumentJson) {
|
|
595
|
+
parsed.repairedLayeredDocumentJson = readText(parsed.repairedLayeredDocumentPath);
|
|
596
|
+
delete parsed.repairedLayeredDocumentPath;
|
|
597
|
+
}
|
|
598
|
+
return { parsedArguments: parsed, argumentsJson: JSON.stringify(parsed) };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function defaultOutputPath(toolName, id, key) {
|
|
602
|
+
const suffix = key === 'renderPngBase64' ? 'render.png' : key === 'diffPngBase64' ? 'diff.png' : key.replace(/Base64$/, '');
|
|
603
|
+
return path.join('tool-output', toolName + '-' + id + '-' + suffix);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function writeBase64Image(output, key, targetPath, toolName, id) {
|
|
607
|
+
if (!output || typeof output !== 'object' || typeof output[key] !== 'string') {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const outputPath = typeof targetPath === 'string' && targetPath.trim()
|
|
611
|
+
? targetPath
|
|
612
|
+
: defaultOutputPath(toolName, id, key);
|
|
613
|
+
const resolved = path.resolve(process.cwd(), outputPath);
|
|
614
|
+
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
615
|
+
fs.writeFileSync(resolved, Buffer.from(output[key], 'base64'));
|
|
616
|
+
output[key.replace(/Base64$/, 'Path')] = outputPath;
|
|
617
|
+
delete output[key];
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function materializeOutputFiles(rawOutput, parsedArguments, id) {
|
|
621
|
+
let output;
|
|
622
|
+
try {
|
|
623
|
+
output = JSON.parse(String(rawOutput || ''));
|
|
624
|
+
} catch {
|
|
625
|
+
return String(rawOutput || '');
|
|
626
|
+
}
|
|
627
|
+
writeBase64Image(output, 'renderPngBase64', parsedArguments.renderPngPath, toolName, id);
|
|
628
|
+
writeBase64Image(output, 'diffPngBase64', parsedArguments.diffPngPath, toolName, id);
|
|
629
|
+
return JSON.stringify(output, null, 2);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
async function main() {
|
|
633
|
+
process.chdir(workspaceDir);
|
|
634
|
+
fs.mkdirSync(requestDir, { recursive: true });
|
|
635
|
+
fs.mkdirSync(responseDir, { recursive: true });
|
|
636
|
+
const id = String(Date.now()) + '-' + String(process.pid) + '-' + Math.random().toString(16).slice(2);
|
|
637
|
+
const fileName = id + '.json';
|
|
638
|
+
const toolArguments = readArgumentsJson();
|
|
639
|
+
fs.writeFileSync(
|
|
640
|
+
path.join(requestDir, fileName),
|
|
641
|
+
JSON.stringify({ id, toolName, argumentsJson: toolArguments.argumentsJson }),
|
|
642
|
+
'utf8'
|
|
643
|
+
);
|
|
644
|
+
|
|
645
|
+
const responsePath = path.join(responseDir, fileName);
|
|
646
|
+
const deadline = Date.now() + 300000;
|
|
647
|
+
while (Date.now() < deadline) {
|
|
648
|
+
if (fs.existsSync(responsePath)) {
|
|
649
|
+
const response = JSON.parse(fs.readFileSync(responsePath, 'utf8'));
|
|
650
|
+
if (!response.ok) {
|
|
651
|
+
console.error(response.error || 'Tool failed.');
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
654
|
+
const output = materializeOutputFiles(response.output, toolArguments.parsedArguments, id);
|
|
655
|
+
process.stdout.write(output);
|
|
656
|
+
if (!output.endsWith('\\n')) process.stdout.write('\\n');
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
sleep(50);
|
|
660
|
+
}
|
|
661
|
+
console.error('Timed out waiting for tool result.');
|
|
662
|
+
process.exit(1);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
main().catch(error => {
|
|
666
|
+
console.error(error && error.message ? error.message : String(error));
|
|
667
|
+
process.exit(1);
|
|
668
|
+
});
|
|
669
|
+
`;
|
|
670
|
+
}
|
|
671
|
+
function normalizeCodexToolArguments(value, toolName) {
|
|
672
|
+
if (typeof value !== 'string') {
|
|
673
|
+
throw new Error(`Codex tool call ${toolName} must provide argumentsJson as a JSON string.`);
|
|
674
|
+
}
|
|
675
|
+
const parsed = JSON.parse(value);
|
|
676
|
+
if (!isRecord(parsed)) {
|
|
677
|
+
throw new Error(`Codex tool call ${toolName} argumentsJson must decode to a JSON object.`);
|
|
678
|
+
}
|
|
679
|
+
return JSON.stringify(parsed);
|
|
680
|
+
}
|
|
681
|
+
function parseCodexToolAction(content, tools, settings, terminalToolNames = []) {
|
|
682
|
+
const parsed = JSON.parse(content);
|
|
683
|
+
if (!isRecord(parsed)) {
|
|
684
|
+
throw new Error('Codex tool response must be a JSON object.');
|
|
685
|
+
}
|
|
686
|
+
const toolChoice = settings?.tool_choice;
|
|
687
|
+
const toolNames = new Set(getToolNames(tools));
|
|
688
|
+
const action = parsed.action;
|
|
689
|
+
if (action === 'final_response') {
|
|
690
|
+
if (terminalToolNames.length > 0) {
|
|
691
|
+
throw new Error(`Codex returned final_response when terminal tool ${terminalToolNames.join(' or ')} was required.`);
|
|
692
|
+
}
|
|
693
|
+
if (toolChoice === 'required') {
|
|
694
|
+
throw new Error('Codex returned final_response when tool_choice required a tool call.');
|
|
695
|
+
}
|
|
696
|
+
if (typeof toolChoice === 'object' && toolChoice?.type === 'function' && toolChoice.function?.name) {
|
|
697
|
+
throw new Error(`Codex returned final_response when ${toolChoice.function.name} was required.`);
|
|
698
|
+
}
|
|
699
|
+
return {
|
|
700
|
+
action: 'final_response',
|
|
701
|
+
content: typeof parsed.finalResponse === 'string' ? parsed.finalResponse : '',
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
if (action !== 'tool_calls') {
|
|
705
|
+
throw new Error(`Codex tool response action must be tool_calls or final_response; received ${String(action)}.`);
|
|
706
|
+
}
|
|
707
|
+
if (toolChoice === 'none') {
|
|
708
|
+
throw new Error('Codex returned tool_calls when tool_choice forbids tool use.');
|
|
709
|
+
}
|
|
710
|
+
if (!Array.isArray(parsed.toolCalls) || parsed.toolCalls.length === 0) {
|
|
711
|
+
throw new Error('Codex tool response must include at least one tool call.');
|
|
712
|
+
}
|
|
713
|
+
const requiredToolName = typeof toolChoice === 'object' && toolChoice?.type === 'function' && toolChoice.function?.name
|
|
714
|
+
? toolChoice.function.name
|
|
715
|
+
: undefined;
|
|
716
|
+
const toolCalls = parsed.toolCalls.map((rawCall, index) => {
|
|
717
|
+
if (!isRecord(rawCall)) {
|
|
718
|
+
throw new Error(`Codex tool call at index ${index} must be an object.`);
|
|
719
|
+
}
|
|
720
|
+
const name = rawCall.name;
|
|
721
|
+
if (typeof name !== 'string' || !toolNames.has(name)) {
|
|
722
|
+
throw new Error(`Codex requested unknown tool ${String(name)}.`);
|
|
723
|
+
}
|
|
724
|
+
if (requiredToolName && name !== requiredToolName) {
|
|
725
|
+
throw new Error(`Codex requested ${name}, but ${requiredToolName} was required.`);
|
|
726
|
+
}
|
|
727
|
+
const id = `codex_tool_${(0, crypto_1.randomUUID)()}`;
|
|
728
|
+
return {
|
|
729
|
+
id,
|
|
730
|
+
call_id: id,
|
|
731
|
+
type: 'function',
|
|
732
|
+
function: {
|
|
733
|
+
name,
|
|
734
|
+
arguments: normalizeCodexToolArguments(rawCall.argumentsJson, name),
|
|
735
|
+
},
|
|
736
|
+
};
|
|
737
|
+
});
|
|
738
|
+
return {
|
|
739
|
+
action: 'tool_calls',
|
|
740
|
+
toolCalls,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
async function readOptionalUtf8File(filePath) {
|
|
744
|
+
try {
|
|
745
|
+
return await (0, promises_1.readFile)(filePath, 'utf8');
|
|
746
|
+
}
|
|
747
|
+
catch (error) {
|
|
748
|
+
if (error.code === 'ENOENT') {
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
throw error;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
242
754
|
function serializeCodexExecError(error) {
|
|
243
755
|
if (!(error instanceof Error))
|
|
244
756
|
return error;
|
|
@@ -362,30 +874,60 @@ async function runCodexExec(options) {
|
|
|
362
874
|
});
|
|
363
875
|
return { stdout, stderr, diagnostics };
|
|
364
876
|
}
|
|
365
|
-
function disabledFeatureArgs(
|
|
366
|
-
return CODEX_DISABLED_FEATURES.filter(feature =>
|
|
877
|
+
function disabledFeatureArgs(options = {}) {
|
|
878
|
+
return CODEX_DISABLED_FEATURES.filter(feature => {
|
|
879
|
+
if (options.allowImageGeneration && feature === 'image_generation')
|
|
880
|
+
return false;
|
|
881
|
+
if (options.allowShellTool && feature === 'shell_tool')
|
|
882
|
+
return false;
|
|
883
|
+
return true;
|
|
884
|
+
}).flatMap(feature => ['--disable', feature]);
|
|
367
885
|
}
|
|
368
|
-
async function executeCodexRequest(messages, model, agent, requestId) {
|
|
369
|
-
if (agent.
|
|
370
|
-
throw new Error('Codex provider v1 does not support
|
|
886
|
+
async function executeCodexRequest(messages, model, agent, requestId, options = {}) {
|
|
887
|
+
if (agent.params || agent.processParams) {
|
|
888
|
+
throw new Error('Codex provider v1 does not support params requests.');
|
|
371
889
|
}
|
|
372
890
|
const settings = agent.modelSettings;
|
|
891
|
+
const codexSettings = getCodexProviderSettings(settings);
|
|
892
|
+
const requestJsonSchema = options.jsonSchema ?? settings?.json_schema;
|
|
373
893
|
const { model: codexModel, effort } = resolveCodexModel(model, settings);
|
|
374
894
|
const codexHome = resolveCodexHome(settings);
|
|
375
|
-
const cwd = agent.cwd || process.cwd();
|
|
376
895
|
const tempDir = await (0, promises_1.mkdtemp)(path_1.default.join((0, os_1.tmpdir)(), 'ensemble-codex-'));
|
|
896
|
+
const cwd = options.useTempCwd ? tempDir : agent.cwd || process.cwd();
|
|
377
897
|
const imageWriter = new codex_assets_js_1.CodexImageAttachmentWriter(tempDir, cwd);
|
|
378
|
-
|
|
898
|
+
let filesystemToolBroker = null;
|
|
899
|
+
const workspaceFileNames = await writeCodexWorkspaceFiles(tempDir, codexSettings?.codex_workspace_files);
|
|
900
|
+
if (options.filesystemTools?.length) {
|
|
901
|
+
filesystemToolBroker = await startCodexFilesystemToolBroker(tempDir, options.filesystemTools, agent);
|
|
902
|
+
}
|
|
903
|
+
const extraInstructions = [
|
|
904
|
+
buildCodexWorkspaceFileInstructions(workspaceFileNames, codexSettings?.codex_workspace_instructions),
|
|
905
|
+
filesystemToolBroker?.instructions,
|
|
906
|
+
options.extraInstructions,
|
|
907
|
+
]
|
|
908
|
+
.map(value => value?.trim() ?? '')
|
|
909
|
+
.filter(Boolean)
|
|
910
|
+
.join('\n\n');
|
|
911
|
+
const requestAgent = extraInstructions
|
|
912
|
+
? {
|
|
913
|
+
...agent,
|
|
914
|
+
instructions: [agent.instructions, extraInstructions]
|
|
915
|
+
.map(value => value?.trim() ?? '')
|
|
916
|
+
.filter(Boolean)
|
|
917
|
+
.join('\n\n'),
|
|
918
|
+
}
|
|
919
|
+
: agent;
|
|
920
|
+
const { instructions, prompt, images } = await buildCodexInput(messages, requestAgent, imageWriter);
|
|
379
921
|
const hasInstructions = instructions.trim().length > 0;
|
|
380
922
|
const instructionsPath = path_1.default.join(tempDir, 'instructions.md');
|
|
381
923
|
const lastMessagePath = path_1.default.join(tempDir, 'last-message.json');
|
|
382
|
-
const schemaPath =
|
|
924
|
+
const schemaPath = requestJsonSchema?.schema ? path_1.default.join(tempDir, 'schema.json') : undefined;
|
|
383
925
|
try {
|
|
384
926
|
if (hasInstructions) {
|
|
385
927
|
await (0, promises_1.writeFile)(instructionsPath, instructions, 'utf8');
|
|
386
928
|
}
|
|
387
929
|
if (schemaPath) {
|
|
388
|
-
await (0, promises_1.writeFile)(schemaPath, JSON.stringify(
|
|
930
|
+
await (0, promises_1.writeFile)(schemaPath, JSON.stringify(requestJsonSchema.schema, null, 2), 'utf8');
|
|
389
931
|
}
|
|
390
932
|
const commandArgs = [
|
|
391
933
|
'exec',
|
|
@@ -394,7 +936,7 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
394
936
|
'--ignore-user-config',
|
|
395
937
|
'--ignore-rules',
|
|
396
938
|
'--skip-git-repo-check',
|
|
397
|
-
...disabledFeatureArgs(),
|
|
939
|
+
...disabledFeatureArgs({ allowShellTool: options.allowShellTool }),
|
|
398
940
|
'-m',
|
|
399
941
|
codexModel,
|
|
400
942
|
'-c',
|
|
@@ -402,19 +944,21 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
402
944
|
...(hasInstructions ? ['-c', `model_instructions_file=${JSON.stringify(instructionsPath)}`] : []),
|
|
403
945
|
...(schemaPath ? ['--output-schema', schemaPath] : []),
|
|
404
946
|
...(images.length > 0 ? ['--image', images.join(',')] : []),
|
|
947
|
+
...(options.allowShellTool ? ['--sandbox', 'workspace-write', '--add-dir', tempDir] : []),
|
|
405
948
|
'--output-last-message',
|
|
406
949
|
lastMessagePath,
|
|
407
950
|
'--cd',
|
|
408
951
|
cwd,
|
|
409
952
|
'-',
|
|
410
953
|
];
|
|
411
|
-
const loggedRequestId = (0, llm_logger_js_1.log_llm_request)(agent.agent_id || 'default', 'codex',
|
|
954
|
+
const loggedRequestId = (0, llm_logger_js_1.log_llm_request)(agent.agent_id || 'default', 'codex', model, {
|
|
412
955
|
command: 'codex',
|
|
413
956
|
args: commandArgs,
|
|
414
957
|
cwd,
|
|
415
958
|
prompt,
|
|
416
959
|
images,
|
|
417
|
-
|
|
960
|
+
codex_model: codexModel,
|
|
961
|
+
schema: requestJsonSchema?.schema,
|
|
418
962
|
}, new Date(), requestId, agent.tags);
|
|
419
963
|
let codexExecDiagnostics;
|
|
420
964
|
try {
|
|
@@ -429,7 +973,9 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
429
973
|
abortSignal: agent.abortSignal,
|
|
430
974
|
});
|
|
431
975
|
codexExecDiagnostics = codexExecResult.diagnostics;
|
|
432
|
-
const usage = codexUsageFromJsonl(codexExecResult.stdout,
|
|
976
|
+
const usage = codexUsageFromJsonl(codexExecResult.stdout, model, loggedRequestId, {
|
|
977
|
+
codex_cli_model: codexModel,
|
|
978
|
+
});
|
|
433
979
|
if (usage)
|
|
434
980
|
cost_tracker_js_1.costTracker.addUsage(usage);
|
|
435
981
|
}
|
|
@@ -447,7 +993,8 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
447
993
|
catch (error) {
|
|
448
994
|
throw new Error(`Codex CLI did not write the expected --output-last-message file: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
449
995
|
}
|
|
450
|
-
const
|
|
996
|
+
const finalFiles = await readCodexWorkspaceFinalFiles(tempDir, codexSettings?.codex_workspace_final_files);
|
|
997
|
+
const content = appendCodexWorkspaceFinalFiles(normalizeLastMessageContent(rawLastMessage, Boolean(schemaPath)), finalFiles);
|
|
451
998
|
(0, llm_logger_js_1.log_llm_response)(loggedRequestId, { content, codex_exec: codexExecDiagnostics });
|
|
452
999
|
return content;
|
|
453
1000
|
}
|
|
@@ -456,9 +1003,81 @@ async function executeCodexRequest(messages, model, agent, requestId) {
|
|
|
456
1003
|
throw error;
|
|
457
1004
|
}
|
|
458
1005
|
finally {
|
|
1006
|
+
await filesystemToolBroker?.stop();
|
|
459
1007
|
await (0, promises_1.rm)(tempDir, { recursive: true, force: true });
|
|
460
1008
|
}
|
|
461
1009
|
}
|
|
1010
|
+
async function* executeCodexToolRequest(messages, model, agent, tools, requestId) {
|
|
1011
|
+
if (agent.modelSettings?.json_schema?.schema) {
|
|
1012
|
+
throw new Error('Codex provider cannot combine simulated tool calls with a caller-supplied json_schema.');
|
|
1013
|
+
}
|
|
1014
|
+
const content = await executeCodexRequest(messages, model, agent, requestId, {
|
|
1015
|
+
extraInstructions: buildCodexToolInstructions(tools, agent),
|
|
1016
|
+
jsonSchema: createCodexToolOutputSchema(tools, agent),
|
|
1017
|
+
});
|
|
1018
|
+
const action = parseCodexToolAction(content, tools, agent.modelSettings, getAvailableTerminalToolNames(tools, agent));
|
|
1019
|
+
if (action.action === 'final_response') {
|
|
1020
|
+
const messageId = `codex-${Date.now()}`;
|
|
1021
|
+
yield {
|
|
1022
|
+
type: 'message_complete',
|
|
1023
|
+
content: action.content,
|
|
1024
|
+
message_id: messageId,
|
|
1025
|
+
};
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
for (const toolCall of action.toolCalls) {
|
|
1029
|
+
yield {
|
|
1030
|
+
type: 'tool_start',
|
|
1031
|
+
tool_call: toolCall,
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
async function* executeCodexFilesystemToolRequest(messages, model, agent, tools, requestId) {
|
|
1036
|
+
if (agent.modelSettings?.json_schema?.schema) {
|
|
1037
|
+
throw new Error('Codex provider cannot combine filesystem tool mode with a caller-supplied json_schema.');
|
|
1038
|
+
}
|
|
1039
|
+
const content = await executeCodexRequest(messages, model, agent, requestId, {
|
|
1040
|
+
allowShellTool: true,
|
|
1041
|
+
filesystemTools: tools,
|
|
1042
|
+
useTempCwd: true,
|
|
1043
|
+
});
|
|
1044
|
+
const messageId = `codex-${Date.now()}`;
|
|
1045
|
+
if (content) {
|
|
1046
|
+
yield {
|
|
1047
|
+
type: 'message_delta',
|
|
1048
|
+
content,
|
|
1049
|
+
message_id: messageId,
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
yield {
|
|
1053
|
+
type: 'message_complete',
|
|
1054
|
+
content,
|
|
1055
|
+
message_id: messageId,
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
async function* executeCodexWorkspaceFileRequest(messages, model, agent, tools, requestId) {
|
|
1059
|
+
if (agent.modelSettings?.json_schema?.schema) {
|
|
1060
|
+
throw new Error('Codex provider cannot combine workspace file mode with a caller-supplied json_schema.');
|
|
1061
|
+
}
|
|
1062
|
+
const content = await executeCodexRequest(messages, model, agent, requestId, {
|
|
1063
|
+
allowShellTool: true,
|
|
1064
|
+
filesystemTools: tools,
|
|
1065
|
+
useTempCwd: true,
|
|
1066
|
+
});
|
|
1067
|
+
const messageId = `codex-${Date.now()}`;
|
|
1068
|
+
if (content) {
|
|
1069
|
+
yield {
|
|
1070
|
+
type: 'message_delta',
|
|
1071
|
+
content,
|
|
1072
|
+
message_id: messageId,
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
yield {
|
|
1076
|
+
type: 'message_complete',
|
|
1077
|
+
content,
|
|
1078
|
+
message_id: messageId,
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
462
1081
|
function buildCodexImagePrompt(prompt, opts = {}) {
|
|
463
1082
|
const count = opts.n && opts.n > 0 ? Math.floor(opts.n) : 1;
|
|
464
1083
|
const details = [
|
|
@@ -541,7 +1160,7 @@ async function executeCodexImageGeneration(prompt, model, agent, opts = {}) {
|
|
|
541
1160
|
'--skip-git-repo-check',
|
|
542
1161
|
'--enable',
|
|
543
1162
|
'image_generation',
|
|
544
|
-
...disabledFeatureArgs(true),
|
|
1163
|
+
...disabledFeatureArgs({ allowImageGeneration: true }),
|
|
545
1164
|
...(promptModelAttempt
|
|
546
1165
|
? [
|
|
547
1166
|
'-m',
|
|
@@ -590,8 +1209,11 @@ async function executeCodexImageGeneration(prompt, model, agent, opts = {}) {
|
|
|
590
1209
|
});
|
|
591
1210
|
throw error;
|
|
592
1211
|
}
|
|
593
|
-
const rawLastMessage = await (
|
|
594
|
-
const
|
|
1212
|
+
const rawLastMessage = await readOptionalUtf8File(lastMessagePath);
|
|
1213
|
+
const lastMessageContent = rawLastMessage ?? '';
|
|
1214
|
+
const responseImagePaths = rawLastMessage
|
|
1215
|
+
? await (0, codex_assets_js_1.extractExistingCodexImagePaths)(rawLastMessage, outputDir)
|
|
1216
|
+
: [];
|
|
595
1217
|
const outputImagePaths = await (0, codex_assets_js_1.newestFirst)(await (0, codex_assets_js_1.listCodexOutputImages)(outputDir));
|
|
596
1218
|
const generatedImagePaths = await (0, codex_assets_js_1.newestFirst)(await (0, codex_assets_js_1.listCodexGeneratedImages)(isolatedCodexHome));
|
|
597
1219
|
const selectedImagePaths = [];
|
|
@@ -603,8 +1225,9 @@ async function executeCodexImageGeneration(prompt, model, agent, opts = {}) {
|
|
|
603
1225
|
break;
|
|
604
1226
|
}
|
|
605
1227
|
if (selectedImagePaths.length < expectedImageCount) {
|
|
606
|
-
const lastMessage =
|
|
607
|
-
|
|
1228
|
+
const lastMessage = lastMessageContent.trim();
|
|
1229
|
+
const lastMessageNote = rawLastMessage === null ? ' Codex CLI did not write --output-last-message.' : '';
|
|
1230
|
+
throw new Error(`Codex image generation resolved ${selectedImagePaths.length} image artifact${selectedImagePaths.length === 1 ? '' : 's'}, expected ${expectedImageCount}.${lastMessageNote}${lastMessage ? ` Last message: ${lastMessage}` : ''}`);
|
|
608
1231
|
}
|
|
609
1232
|
const generatedImages = await (0, codex_assets_js_1.readCodexImageFiles)(selectedImagePaths);
|
|
610
1233
|
(0, llm_logger_js_1.log_llm_response)(loggedRequestId, {
|
|
@@ -613,7 +1236,8 @@ async function executeCodexImageGeneration(prompt, model, agent, opts = {}) {
|
|
|
613
1236
|
response_image_paths: responseImagePaths,
|
|
614
1237
|
output_image_paths: outputImagePaths,
|
|
615
1238
|
codex_home_image_paths: generatedImagePaths,
|
|
616
|
-
last_message:
|
|
1239
|
+
last_message: lastMessageContent.trim(),
|
|
1240
|
+
last_message_missing: rawLastMessage === null,
|
|
617
1241
|
codex_exec: codexExecDiagnostics,
|
|
618
1242
|
});
|
|
619
1243
|
cost_tracker_js_1.costTracker.addUsage({
|
|
@@ -653,6 +1277,21 @@ class CodexProvider extends base_provider_js_1.BaseModelProvider {
|
|
|
653
1277
|
}
|
|
654
1278
|
async *createResponseStream(messages, model, agent, requestId) {
|
|
655
1279
|
try {
|
|
1280
|
+
const { getToolsFromAgent } = await Promise.resolve().then(() => __importStar(require("../utils/agent.cjs")));
|
|
1281
|
+
const tools = agent ? await getToolsFromAgent(agent) : [];
|
|
1282
|
+
if (tools.length > 0) {
|
|
1283
|
+
const codexToolTransport = getCodexProviderSettings(agent.modelSettings)?.codex_tool_transport;
|
|
1284
|
+
if (codexToolTransport === 'workspace-files') {
|
|
1285
|
+
yield* executeCodexWorkspaceFileRequest(messages, model, agent, tools, requestId);
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
if (codexToolTransport === 'filesystem') {
|
|
1289
|
+
yield* executeCodexFilesystemToolRequest(messages, model, agent, tools, requestId);
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
yield* executeCodexToolRequest(messages, model, agent, tools, requestId);
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
656
1295
|
const content = await executeCodexRequest(messages, model, agent, requestId);
|
|
657
1296
|
const messageId = `codex-${Date.now()}`;
|
|
658
1297
|
if (content) {
|