diff --git a/lib/agent-runtime.js b/lib/agent-runtime.js index a6795a3..ee9551d 100644 --- a/lib/agent-runtime.js +++ b/lib/agent-runtime.js @@ -73,16 +73,22 @@ function createAgentRuntime(deps) { } const runtimeId = getRuntimeSessionId(session); const args = ['exec']; - if (runtimeId) args.push('resume'); args.push('--json', '--skip-git-repo-check'); const permMode = session.permissionMode || 'yolo'; + // `-s/--sandbox` is an option for `codex exec`, but not for `codex exec resume`. + // When resuming, it must appear before the `resume` subcommand, otherwise Codex CLI errors + // with: "unexpected argument '-s' found". + if (runtimeId && permMode === 'plan') { + args.push('-s', 'read-only'); + } + if (runtimeId) args.push('resume'); switch (permMode) { case 'yolo': args.push('--dangerously-bypass-approvals-and-sandbox'); break; case 'plan': - args.push('-s', 'read-only'); + if (!runtimeId) args.push('-s', 'read-only'); break; case 'default': default: diff --git a/scripts/mock-codex.js b/scripts/mock-codex.js index a014c99..646bc70 100755 --- a/scripts/mock-codex.js +++ b/scripts/mock-codex.js @@ -16,7 +16,8 @@ function readStdin() { (async function main() { const args = process.argv.slice(2); - const isResume = args[0] === 'exec' && args[1] === 'resume'; + // cc-web can place `resume` after other `codex exec` options (e.g. --json, -s). + const isResume = args[0] === 'exec' && args.includes('resume'); const threadId = (() => { if (!isResume) return `mock-${crypto.randomUUID()}`; for (let i = args.length - 1; i >= 2; i--) { diff --git a/scripts/regression.js b/scripts/regression.js index cd7dac8..f0a4023 100644 --- a/scripts/regression.js +++ b/scripts/regression.js @@ -398,8 +398,9 @@ async function main() { .split('\n') .filter((line) => line.includes(`"event":"process_spawn"`) && line.includes(firstMessageSession.sessionId.slice(0, 8))); const lastSpawn = allSpawnsForSession[allSpawnsForSession.length - 1] || ''; - assert(lastSpawn.includes('exec resume') && lastSpawn.includes(threadIdBeforeMode), 'Codex mode switch should keep resume thread id'); + assert(lastSpawn.includes('resume') && lastSpawn.includes(threadIdBeforeMode), 'Codex mode switch should keep resume thread id'); assert(lastSpawn.includes('-s read-only'), 'Codex plan mode should set sandbox read-only'); + assert(lastSpawn.includes('-s read-only resume'), 'Codex resume in plan mode must place -s before resume subcommand'); const runtimeToml = fs.readFileSync(path.join(configDir, 'codex-runtime-home', 'config.toml'), 'utf8'); assert(runtimeToml.includes('preferred_auth_method = "apikey"'), 'Codex custom profile should write isolated runtime auth mode');