fix(codex): stop passing retired approval_policy=untrusted to Codex CLI

Codex CLI v0.149.0 removed the 'untrusted' approval policy - its
ask-before-run semantics were merged into 'on-request'. The plugin
still mapped default/plan/sandbox permission modes to
approval_policy=untrusted, so every message failed with:
  Error: approval_policy = "untrusted" is no longer supported;
  remove this setting
and Codex became completely unusable after upgrading the CLI
(v0.146.0 -> v0.149.0) (#1702).

- permission-mapper.js: SANDBOX / DEFAULT now map to on-request
- CodexSDKBridge.java: plan / default env override now injects
  on-request instead of untrusted
- codex-utils.js: drop 'untrusted' from VALID_APPROVAL_POLICIES so
  stale env values are ignored instead of forwarded
- i18n: update codexModes.default.tooltip in all 10 locales

'on-request' is supported by both old and new CLI versions, so this
is safe for all users.
This commit is contained in:
hebulin
2026-08-21 14:48:44 +08:00
parent 077cccff67
commit a3f67fd90c
14 changed files with 83 additions and 16 deletions
+4 -1
View File
@@ -30,7 +30,10 @@ export const logWarn = (tag, ...args) => debugLog(2, tag, ...args);
export const logInfo = (tag, ...args) => debugLog(3, tag, ...args);
export const logDebug = (tag, ...args) => debugLog(4, tag, ...args);
export const VALID_SANDBOX_MODES = new Set(['read-only', 'workspace-write', 'danger-full-access']);
export const VALID_APPROVAL_POLICIES = new Set(['never', 'on-request', 'on-failure', 'untrusted']);
export const VALID_APPROVAL_POLICIES = new Set(['never', 'on-request', 'on-failure']);
// Note: 'untrusted' was removed in Codex CLI v0.149.0 - its semantics were merged
// into 'on-request'. Passing it makes new CLI versions exit with
// "approval_policy = \"untrusted\" is no longer supported; remove this setting" (#1702).
export const CODEX_CLI_ENV_BLOCKLIST = new Set([
'CODEX_APPROVAL_POLICY',
'CODEX_SANDBOX_MODE',
+5 -2
View File
@@ -155,10 +155,12 @@ export class CodexPermissionMapper {
switch (core) {
case UnifiedPermissionMode.SANDBOX:
// Sandbox: Read-only mode (always prompt when attempting to write)
// 'untrusted' was removed in Codex CLI v0.149 - its ask-before-run semantics
// were merged into 'on-request', which older CLI versions also support (#1702).
return {
skipGitRepoCheck: true,
sandbox: 'read-only',
approvalPolicy: 'untrusted'
approvalPolicy: 'on-request'
};
case UnifiedPermissionMode.YOLO:
@@ -173,10 +175,11 @@ export class CodexPermissionMapper {
default:
// Default: Allow workspace writes but still prompt before executing risky actions
// On Windows, use danger-full-access since sandbox is experimental
// 'untrusted' was removed in Codex CLI v0.149 - see the SANDBOX case above (#1702).
return {
skipGitRepoCheck: true,
sandbox: onWindows ? 'danger-full-access' : 'workspace-write',
approvalPolicy: 'untrusted'
approvalPolicy: 'on-request'
};
}
}
+59
View File
@@ -0,0 +1,59 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { UnifiedPermissionMode, CodexPermissionMapper } from './permission-mapper.js';
import { VALID_APPROVAL_POLICIES } from '../services/codex/codex-utils.js';
// ---------- CodexPermissionMapper.toProvider (#1702: 'untrusted' retired) ----------
test('toProvider never maps any unified mode to the removed untrusted policy', () => {
const modes = [
UnifiedPermissionMode.DEFAULT,
UnifiedPermissionMode.SANDBOX,
UnifiedPermissionMode.YOLO,
'bypassPermissions',
'acceptEdits',
'autoEdit',
'plan',
'unknown-mode',
null,
undefined,
];
for (const mode of modes) {
const config = CodexPermissionMapper.toProvider(mode);
assert.ok(
config.approvalPolicy !== 'untrusted',
`mode=${mode} must not map to the removed 'untrusted' policy (got: ${config.approvalPolicy})`,
);
assert.ok(
VALID_APPROVAL_POLICIES.has(config.approvalPolicy),
`mode=${mode} approvalPolicy ${config.approvalPolicy} must be in VALID_APPROVAL_POLICIES`,
);
}
});
test('toProvider maps default and sandbox to on-request (untrusted semantics successor)', () => {
// Codex CLI v0.149 removed 'untrusted'; its ask-before-run semantics now live in
// 'on-request'. Older CLI versions support 'on-request' as well, so it is safe
// for both (#1702).
assert.equal(CodexPermissionMapper.toProvider(UnifiedPermissionMode.DEFAULT).approvalPolicy, 'on-request');
assert.equal(CodexPermissionMapper.toProvider(UnifiedPermissionMode.SANDBOX).approvalPolicy, 'on-request');
assert.equal(CodexPermissionMapper.toProvider('plan').approvalPolicy, 'on-request');
});
test('toProvider keeps yolo / acceptEdits mappings unchanged', () => {
assert.equal(CodexPermissionMapper.toProvider(UnifiedPermissionMode.YOLO).approvalPolicy, 'never');
assert.equal(CodexPermissionMapper.toProvider('bypassPermissions').approvalPolicy, 'never');
assert.equal(CodexPermissionMapper.toProvider('acceptEdits').approvalPolicy, 'on-request');
assert.equal(CodexPermissionMapper.toProvider('autoEdit').approvalPolicy, 'on-request');
});
// ---------- VALID_APPROVAL_POLICIES whitelist ----------
test('VALID_APPROVAL_POLICIES no longer accepts the removed untrusted value', () => {
assert.equal(VALID_APPROVAL_POLICIES.has('untrusted'), false);
assert.equal(VALID_APPROVAL_POLICIES.has('on-request'), true);
assert.equal(VALID_APPROVAL_POLICIES.has('never'), true);
assert.equal(VALID_APPROVAL_POLICIES.has('on-failure'), true);
});
@@ -45,7 +45,9 @@ public class CodexSDKBridge extends BaseSDKBridge {
private static final String SANDBOX_MODE_READ_ONLY = "read-only";
private static final String APPROVAL_POLICY_NEVER = "never";
private static final String APPROVAL_POLICY_ON_REQUEST = "on-request";
private static final String APPROVAL_POLICY_UNTRUSTED = "untrusted";
// Note: 'untrusted' was removed in Codex CLI v0.149.0 - its semantics were merged
// into 'on-request'. Injecting it makes new CLI versions exit with
// "approval_policy = \"untrusted\" is no longer supported" (#1702).
private static final String ENV_CODEX_APPROVAL_POLICY = "CODEX_APPROVAL_POLICY";
private static final String ENV_CODEX_SANDBOX_MODE = "CODEX_SANDBOX_MODE";
private static final String ENV_CODEX_SANDBOX = "CODEX_SANDBOX";
@@ -501,13 +503,13 @@ public class CodexSDKBridge extends BaseSDKBridge {
case "plan":
env.put(ENV_CODEX_SANDBOX_MODE, sandboxMode);
env.put(ENV_CODEX_SANDBOX, sandboxMode);
env.put(ENV_CODEX_APPROVAL_POLICY, APPROVAL_POLICY_UNTRUSTED);
env.put(ENV_CODEX_APPROVAL_POLICY, APPROVAL_POLICY_ON_REQUEST);
break;
default:
// Default mode: use configured sandbox mode with confirmation
env.put(ENV_CODEX_SANDBOX_MODE, sandboxMode);
env.put(ENV_CODEX_SANDBOX, sandboxMode);
env.put(ENV_CODEX_APPROVAL_POLICY, APPROVAL_POLICY_UNTRUSTED);
env.put(ENV_CODEX_APPROVAL_POLICY, APPROVAL_POLICY_ON_REQUEST);
break;
}
LOG.info("[Codex] Permission env override: SANDBOX_MODE=" +
+1 -1
View File
@@ -1996,7 +1996,7 @@
"codexModes": {
"default": {
"label": "Suggest (approval)",
"tooltip": "Codex approval_policy=untrusted prompts before editing files or running shell commands.",
"tooltip": "Codex approval_policy=on-request prompts before editing files or running shell commands.",
"description": "Safest option. Every write or command requires your approval."
},
"plan": {
+1 -1
View File
@@ -1620,7 +1620,7 @@
"codexModes": {
"default": {
"label": "Suggest (approval)",
"tooltip": "Codex approval_policy=untrusted prompts before editing files or running shell commands.",
"tooltip": "Codex approval_policy=on-request prompts before editing files or running shell commands.",
"description": "Safest option. Every write or command requires your approval."
},
"plan": {
+1 -1
View File
@@ -1620,7 +1620,7 @@
"codexModes": {
"default": {
"label": "Suggest (approval)",
"tooltip": "Codex approval_policy=untrusted prompts before editing files or running shell commands.",
"tooltip": "Codex approval_policy=on-request prompts before editing files or running shell commands.",
"description": "Safest option. Every write or command requires your approval."
},
"plan": {
+1 -1
View File
@@ -1619,7 +1619,7 @@
"codexModes": {
"default": {
"label": "Suggest (approval)",
"tooltip": "Codex approval_policy=untrusted prompts before editing files or running shell commands.",
"tooltip": "Codex approval_policy=on-request prompts before editing files or running shell commands.",
"description": "Safest option. Every write or command requires your approval."
},
"plan": {
+1 -1
View File
@@ -1625,7 +1625,7 @@
"codexModes": {
"default": {
"label": "Suggest (approval)",
"tooltip": "Codex approval_policy=untrusted prompts before editing files or running shell commands.",
"tooltip": "Codex approval_policy=on-request prompts before editing files or running shell commands.",
"description": "Safest option. Every write or command requires your approval."
},
"plan": {
+1 -1
View File
@@ -1715,7 +1715,7 @@
"codexModes": {
"default": {
"label": "제안 (승인 필요)",
"tooltip": "Codex approval_policy=untrusted – 파일 편집이나 셸 명령 실행 전에 확인합니다.",
"tooltip": "Codex approval_policy=on-request – 파일 편집이나 셸 명령 실행 전에 확인합니다.",
"description": "가장 안전한 옵션. 모든 쓰기 또는 명령에 승인이 필요합니다."
},
"plan": {
+1 -1
View File
@@ -1770,7 +1770,7 @@
"codexModes": {
"default": {
"label": "Sugerir (aprovação)",
"tooltip": "Codex approval_policy=untrusted solicita antes de editar arquivos ou executar comandos shell.",
"tooltip": "Codex approval_policy=on-request solicita antes de editar arquivos ou executar comandos shell.",
"description": "Opção mais segura. Cada escrita ou comando requer sua aprovação."
},
"plan": {
+1 -1
View File
@@ -1646,7 +1646,7 @@
"codexModes": {
"default": {
"label": "Предложение (одобрение)",
"tooltip": "Codex approval_policy=untrusted — запрашивает перед редактированием файлов или выполнением команд.",
"tooltip": "Codex approval_policy=on-request - запрашивает перед редактированием файлов или выполнением команд.",
"description": "Самый безопасный вариант. Каждая запись или команда требует вашего одобрения."
},
"plan": {
+1 -1
View File
@@ -1625,7 +1625,7 @@
"codexModes": {
"default": {
"label": "Suggest (approval)",
"tooltip": "Codex approval_policy=untrusted prompts before editing files or running shell commands.",
"tooltip": "Codex approval_policy=on-request prompts before editing files or running shell commands.",
"description": "Safest option. Every write or command requires your approval."
},
"plan": {
+1 -1
View File
@@ -2001,7 +2001,7 @@
"codexModes": {
"default": {
"label": "建议模式",
"tooltip": "Codex approval_policy=untrusted:执行命令或写文件前都会弹窗确认。",
"tooltip": "Codex approval_policy=on-request:执行命令或写文件前都会弹窗确认。",
"description": "最安全的选择,每一步都需要你亲自批准。"
},
"plan": {