fix(orchestration): integrate supported Codex worker invocation (#3434)

This commit is contained in:
haelyra
2026-10-10 17:36:45 -04:00
2 changed files with 88 additions and 12 deletions
+10 -12
View File
@@ -48,20 +48,16 @@ fi
write_status "running" "- Task file: \`$task_file\`"
# SECURITY: never auto-approve agent tool execution. The worker prompt is built
# from a task file that may contain LLM-generated or third-party content
# (indirect prompt injection). `codex exec -p yolo` would execute
# rm -rf / exfiltration commands without confirmation.
# Default to the most restrictive approval mode; allow an explicit operator
# override only via env (e.g. ECC_CODEX_APPROVAL_MODE=on-request for trusted runs).
# Codex profiles (-p) and approval policies (--ask-for-approval) are
# independent concepts. SECURITY: default to never approving untrusted
# tool execution; operators can override via env.
# Keep the configured sandbox; do not bypass approvals or sandboxing for task
# files that may contain third-party content. Non-interactive runs default to
# never prompting for approvals. This does not disable commands allowed by the
# sandbox. Operators can request approvals with ECC_CODEX_APPROVAL_POLICY.
# Approval policies (--ask-for-approval) are separate from Codex profiles (-p).
APPROVAL_POLICY="${ECC_CODEX_APPROVAL_POLICY:-never}"
case "$APPROVAL_POLICY" in
never|on-request|on-failure) ;;
never|on-request) ;;
*)
echo "[ECC worker] Refusing to run: unsupported ECC_CODEX_APPROVAL_POLICY='$APPROVAL_POLICY' (expected never|on-request|on-failure)" >&2
echo "[ECC worker] Refusing to run: unsupported ECC_CODEX_APPROVAL_POLICY='$APPROVAL_POLICY' (expected never|on-request)" >&2
write_status "failed" "- Error: unsupported approval policy"
exit 1
;;
@@ -109,7 +105,9 @@ Task file: $task_file
$(cat "$task_file")
EOF
if codex exec --ask-for-approval "$APPROVAL_POLICY" -m gpt-5.4 --color never -C "$(pwd)" -o "$output_file" - < "$prompt_file"; then
# Approval is a global option. Inherit the operator's configured model instead
# of pinning a model that may be unavailable or retired.
if codex --ask-for-approval "$APPROVAL_POLICY" exec --color never -C "$(pwd)" -o "$output_file" - < "$prompt_file"; then
{
echo "# Handoff"
echo
@@ -24,6 +24,84 @@ function test(desc, fn) {
}
}
function runWorker({ policy, exitCode = 0 } = {}) {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-orch-worker-'));
const binDir = path.join(tempRoot, 'bin');
const captureFile = path.join(tempRoot, 'args.json');
const taskFile = path.join(tempRoot, 'task.md');
const handoffFile = path.join(tempRoot, 'handoff.md');
const statusFile = path.join(tempRoot, 'status.md');
try {
fs.mkdirSync(binDir);
fs.writeFileSync(taskFile, 'Make a focused change.');
fs.writeFileSync(path.join(binDir, 'codex'), `#!${process.execPath}
const fs = require('fs');
const args = process.argv.slice(2);
fs.writeFileSync(process.env.ECC_TEST_CAPTURE, JSON.stringify(args));
// Approval is a global option, not an exec option in current Codex CLI.
const execIndex = args.indexOf('exec');
if (execIndex < 0 || args.indexOf('--ask-for-approval') > execIndex) {
console.error("unexpected argument '--ask-for-approval' found");
process.exit(2);
}
const outputIndex = args.indexOf('-o');
fs.writeFileSync(args[outputIndex + 1], 'Completed the focused task.');
process.exit(Number(process.env.ECC_TEST_EXIT));
`, { mode: 0o755 });
spawnSync('git', ['init'], { cwd: tempRoot, stdio: 'ignore' });
const env = { ...process.env,
PATH: `${binDir}${path.delimiter}${process.env.PATH}`,
ECC_TEST_CAPTURE: captureFile,
ECC_TEST_EXIT: String(exitCode)
};
if (policy === undefined) delete env.ECC_CODEX_APPROVAL_POLICY;
else env.ECC_CODEX_APPROVAL_POLICY = policy;
const result = spawnSync('bash', [SCRIPT, taskFile, handoffFile, statusFile], {
cwd: tempRoot, env, encoding: 'utf8', timeout: 10000
});
return {
...result,
args: fs.existsSync(captureFile) ? JSON.parse(fs.readFileSync(captureFile, 'utf8')) : null,
handoff: fs.existsSync(handoffFile) ? fs.readFileSync(handoffFile, 'utf8') : '',
workerStatus: fs.readFileSync(statusFile, 'utf8')
};
} finally {
fs.rmSync(tempRoot, { recursive: true, force: true });
}
}
test('starts a worker with the current CLI grammar and configured model', () => {
const result = runWorker();
assert.strictEqual(result.status, 0, result.stderr);
assert.strictEqual(result.args[result.args.indexOf('--ask-for-approval') + 1], 'never');
assert.ok(!result.args.includes('-m') && !result.args.includes('--model'),
'Worker should inherit the configured model rather than pin a retired model');
assert.ok(result.handoff.includes('Completed the focused task.'));
assert.ok(result.workerStatus.includes('- State: completed'));
});
test('forwards the explicit supported approval policy', () => {
const result = runWorker({ policy: 'on-request' });
assert.strictEqual(result.status, 0, result.stderr);
assert.strictEqual(result.args[result.args.indexOf('--ask-for-approval') + 1], 'on-request');
});
test('rejects the deprecated approval policy before invoking Codex', () => {
const result = runWorker({ policy: 'on-failure' });
assert.notStrictEqual(result.status, 0);
assert.strictEqual(result.args, null);
assert.ok(result.workerStatus.includes('unsupported approval policy'));
});
test('records failure artifacts when a started worker exits nonzero', () => {
const result = runWorker({ exitCode: 1 });
assert.strictEqual(result.status, 1);
assert.ok(result.args);
assert.ok(result.workerStatus.includes('- State: failed'));
assert.ok(result.handoff.includes('The Codex worker exited with a non-zero status.'));
});
test('fails fast for an unreadable task file and records failure artifacts', () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-orch-worker-'));
const handoffFile = path.join(tempRoot, '.orchestration', 'docs', 'handoff.md');