fix(hub,cli): forward permissionMode on session resume (#460)

* feat(hub,cli): forward permissionMode on session resume

When a session is resumed, the cached permissionMode is now forwarded
through the Hub → Runner → CLI pipeline via a new --permission-mode
flag. Previously the mode was lost on resume, resetting to 'default'.

Each CLI flavor validates the flag value against its own allowed
permission modes (e.g. CLAUDE_PERMISSION_MODES) and rejects unknown
values. The existing --yolo flag is preserved as a shorthand.

* refactor(cli): extract buildCliArgs from startRunner

Extract the CLI argument construction logic into a standalone
exported function so it can be unit-tested independently.
No behavior change.

* test(cli): add buildCliArgs unit tests for --permission-mode

Verify that the runner correctly forwards valid permission modes
via --permission-mode, rejects invalid values, and falls back to
--yolo when no permission mode is set.

* fix(cli): let --permission-mode take precedence over --yolo

When both flags are present, --permission-mode was silently
overwritten by a later --yolo. Guard legacy flag branches with
a hasExplicitPermissionMode check so the explicit flag wins.
This commit is contained in:
Junmo Kim
2026-04-15 11:13:21 +08:00
committed by GitHub
parent 09c2c57eaf
commit e6eaff83c5
12 changed files with 239 additions and 45 deletions
+10 -1
View File
@@ -3,6 +3,7 @@ import { authAndSetupMachineIfNeeded } from '@/ui/auth'
import { initializeToken } from '@/ui/tokenInit'
import { maybeAutoStartServer } from '@/utils/autoStartServer'
import type { CommandDefinition } from './types'
import { CODEX_PERMISSION_MODES } from '@hapi/protocol/modes'
import type { CodexPermissionMode } from '@hapi/protocol/types'
import type { ReasoningEffort } from '@/codex/appServerTypes'
@@ -36,6 +37,7 @@ export const codexCommand: CommandDefinition = {
modelReasoningEffort?: ReasoningEffort
} = {}
const unknownArgs: string[] = []
let hasExplicitPermissionMode = false
for (let i = 0; i < commandArgs.length; i++) {
const arg = commandArgs[i]
@@ -50,7 +52,14 @@ export const codexCommand: CommandDefinition = {
}
if (arg === '--started-by') {
options.startedBy = commandArgs[++i] as 'runner' | 'terminal'
} else if (arg === '--yolo' || arg === '--dangerously-bypass-approvals-and-sandbox') {
} else if (arg === '--permission-mode') {
const mode = commandArgs[++i]
if (!mode || !(CODEX_PERMISSION_MODES as readonly string[]).includes(mode)) {
throw new Error(`Invalid --permission-mode value: ${mode ?? '(missing)'}`)
}
options.permissionMode = mode as CodexPermissionMode
hasExplicitPermissionMode = true
} else if ((arg === '--yolo' || arg === '--dangerously-bypass-approvals-and-sandbox') && !hasExplicitPermissionMode) {
options.permissionMode = 'yolo'
unknownArgs.push(arg)
} else if (arg === '--model') {