feat(codex): flag-gated system-default real-home routing scaffolding

Staged internal flag (default OFF, no settings UI): route the SYSTEM-DEFAULT
Codex account at the user's real ~/.codex instead of Orca's managed runtime
home. Flag OFF is byte-identical to today; managed (multi-account) selections
are unchanged in either state.

Routing (flag ON + host system default = no managed account):
- CodexRuntimeHomeService.prepareForCodexLaunch / prepareForRateLimitFetch
  return null so the PTY/env layer injects no managed CODEX_HOME and the
  rate-limit fetcher + auth-presence gate fall back to ~/.codex (the background
  poller stops spawning Codex against the managed home — the #5370 auth war).
- buildPtyHostEnv strips only a nested-Orca-inherited Orca-owned override
  (CODEX_HOME matching the private ORCA_CODEX_HOME marker), preserving a
  user-set CODEX_HOME. Shell-ready re-exports already no-op without the marker.
- The headless commit-message Codex path strips the same inherited override.

Hook install for the real-home lane (append-last into ~/.codex/hooks.json,
trust via the app-server client) lands with the trust plumbing; the managed
hook install is skipped for this lane meanwhile.

Credit @jellychoco (#8606) for the native-home routing direction.

Depends on the codex trust-rpc-grant plumbing for the real-home hook installer.
This commit is contained in:
Brennan Benson
2026-07-14 22:23:24 -07:00
parent d9f7fd08f7
commit 07afc8fbb7
10 changed files with 285 additions and 2 deletions
@@ -991,6 +991,49 @@ describe('CodexRuntimeHomeService', () => {
expect(existsSync(getRuntimeCodexHomePath())).toBe(true)
})
it('routes host system default to the real home (null) when the flag is ON', async () => {
const store = createStore(createSettings({ codexSystemDefaultRealHomeEnabled: true }))
const { CodexRuntimeHomeService } = await import('./runtime-home-service')
const service = new CodexRuntimeHomeService(store as never)
expect(service.isHostSystemDefaultRealHome()).toBe(true)
expect(service.prepareForCodexLaunch()).toBeNull()
expect(service.prepareForRateLimitFetch()).toBeNull()
})
it('keeps the managed home for a host MANAGED account even when the flag is ON', async () => {
const managedHomePath = createManagedAuth(
testState.userDataDir,
'account-1',
'{"account":"managed"}\n'
)
const store = createStore(
createSettings({
codexSystemDefaultRealHomeEnabled: true,
codexManagedAccounts: [
{
id: 'account-1',
email: 'user@example.com',
managedHomePath,
providerAccountId: null,
workspaceLabel: null,
workspaceAccountId: null,
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1
}
],
activeCodexManagedAccountId: 'account-1',
activeCodexManagedAccountIdsByRuntime: { host: 'account-1', wsl: {} }
})
)
const { CodexRuntimeHomeService } = await import('./runtime-home-service')
const service = new CodexRuntimeHomeService(store as never)
expect(service.isHostSystemDefaultRealHome()).toBe(false)
expect(service.prepareForCodexLaunch()).toBe(getRuntimeCodexHomePath())
})
it('uses the same host CODEX_HOME after switching managed Codex accounts', async () => {
const runtimeAuthPath = getRuntimeCodexAuthPath()
const account1Auth = createCodexAuthJson('one@example.com', 'acct-1', 'one')
@@ -60,6 +60,7 @@ import {
type CodexAccountSelectionTarget
} from './runtime-selection'
import { getDefaultWslDistro, getWslHome } from '../wsl'
import { isCodexSystemDefaultRealHomeEnabled } from '../codex/codex-real-home-flag'
type CodexAuthIdentity = {
email: string | null
@@ -147,6 +148,13 @@ export class CodexRuntimeHomeService {
this.startWslSessionBridgeForLaunch(wslTarget, runtimeHomePath)
return runtimeHomePath
}
if (this.isHostSystemDefaultRealHome()) {
// Why (flag ON, system default): run Codex on the user's own ~/.codex.
// Returning null tells the PTY/env layer to inject no managed CODEX_HOME;
// sessions, auth, and config all live in the native home. No system->
// managed session bridge runs, so the real home stays the single source.
return null
}
this.syncForCurrentSelection()
syncSystemCodexResourcesIntoManagedHome()
syncSystemConfigIntoManagedCodexHome()
@@ -192,6 +200,17 @@ export class CodexRuntimeHomeService {
return this.getRuntimeHomePath()
}
// Why: real-home routing applies only to the host system-default selection
// (no managed account chosen for host) with the staged flag ON. Managed host
// accounts keep the isolated runtime home for hot-swap and token persistence.
isHostSystemDefaultRealHome(): boolean {
const settings = this.store.getSettings()
return (
isCodexSystemDefaultRealHomeEnabled(settings) &&
normalizeCodexRuntimeSelection(settings).host === null
)
}
syncActiveWslSelectionsBeforeRestart(): void {
if (process.platform !== 'win32') {
return
@@ -255,6 +274,13 @@ export class CodexRuntimeHomeService {
const syncedRuntimeHomePath = this.getPreparedWslRateLimitHomePath(wslTarget)
return syncedRuntimeHomePath ?? this.getWslSystemCodexHomePath(wslTarget)
}
if (this.isHostSystemDefaultRealHome()) {
// Why (flag ON, system default): read usage/auth from the user's own
// ~/.codex. Returning null makes the fetcher fall back to ~/.codex and
// its auth-presence gate check the real auth.json, so the background
// poller never spawns Codex against the managed home (the #5370 auth war).
return null
}
this.syncForCurrentSelection()
syncSystemCodexResourcesIntoManagedHome()
syncSystemConfigIntoManagedCodexHome()
@@ -0,0 +1,53 @@
import { afterEach, describe, expect, it } from 'vitest'
import { isCodexSystemDefaultRealHomeEnabled } from './codex-real-home-flag'
const ENV_FLAG = 'ORCA_CODEX_SYSTEM_DEFAULT_REAL_HOME'
afterEach(() => {
delete process.env[ENV_FLAG]
})
describe('isCodexSystemDefaultRealHomeEnabled', () => {
it('is OFF by default (undefined settings)', () => {
expect(isCodexSystemDefaultRealHomeEnabled(undefined)).toBe(false)
expect(isCodexSystemDefaultRealHomeEnabled(null)).toBe(false)
expect(isCodexSystemDefaultRealHomeEnabled({})).toBe(false)
})
it('honors the settings flag when set to true', () => {
expect(isCodexSystemDefaultRealHomeEnabled({ codexSystemDefaultRealHomeEnabled: true })).toBe(
true
)
expect(isCodexSystemDefaultRealHomeEnabled({ codexSystemDefaultRealHomeEnabled: false })).toBe(
false
)
})
it('lets the env override force ON regardless of settings', () => {
for (const raw of ['1', 'true', 'on', 'TRUE', ' On ']) {
process.env[ENV_FLAG] = raw
expect(
isCodexSystemDefaultRealHomeEnabled({ codexSystemDefaultRealHomeEnabled: false })
).toBe(true)
}
})
it('lets the env override force OFF regardless of settings', () => {
for (const raw of ['0', 'false', 'off']) {
process.env[ENV_FLAG] = raw
expect(isCodexSystemDefaultRealHomeEnabled({ codexSystemDefaultRealHomeEnabled: true })).toBe(
false
)
}
})
it('ignores an unrecognized env value and falls back to settings', () => {
process.env[ENV_FLAG] = 'maybe'
expect(isCodexSystemDefaultRealHomeEnabled({ codexSystemDefaultRealHomeEnabled: true })).toBe(
true
)
expect(isCodexSystemDefaultRealHomeEnabled({ codexSystemDefaultRealHomeEnabled: false })).toBe(
false
)
})
})
+40
View File
@@ -0,0 +1,40 @@
import type { GlobalSettings } from '../../shared/types'
/**
* Staged internal flag: route the SYSTEM-DEFAULT Codex account at the user's
* real ~/.codex instead of Orca's managed runtime home.
*
* Why a flag: this moves where user Codex state lives (auth, config, sessions,
* hooks). It ships dark, default OFF, with NO settings UI, so flag-OFF stays
* byte-identical to today's managed-home behavior. Managed (multi-account)
* selections are unaffected either way.
*
* The env override exists only so isolated dev/CDP verification can exercise
* the ON path without a settings write; it never appears in the UI.
*/
const CODEX_REAL_HOME_ENV_FLAG = 'ORCA_CODEX_SYSTEM_DEFAULT_REAL_HOME'
export function isCodexSystemDefaultRealHomeEnabled(
settings: Pick<GlobalSettings, 'codexSystemDefaultRealHomeEnabled'> | null | undefined
): boolean {
const envOverride = readCodexRealHomeEnvOverride()
if (envOverride !== null) {
return envOverride
}
return settings?.codexSystemDefaultRealHomeEnabled === true
}
function readCodexRealHomeEnvOverride(): boolean | null {
const raw = process.env[CODEX_REAL_HOME_ENV_FLAG]
if (raw === undefined) {
return null
}
const normalized = raw.trim().toLowerCase()
if (normalized === '1' || normalized === 'true' || normalized === 'on') {
return true
}
if (normalized === '0' || normalized === 'false' || normalized === 'off') {
return false
}
return null
}
+7
View File
@@ -734,6 +734,13 @@ function startTerminalRuntimeStartupServices(): Promise<void> {
function prepareCodexRuntimeHomeForLaunch(target?: CodexAccountSelectionTarget): string | null {
const runtimeHomePath = codexRuntimeHome!.prepareForCodexLaunch(target)
if (runtimeHomePath === null && codexRuntimeHome!.isHostSystemDefaultRealHome()) {
// Why (flag ON, system default): Codex runs on the user's real ~/.codex, so
// the managed-home hook install below would target a home Codex never reads.
// The real-home hook installer (trust granted via the app-server client)
// owns hook install for this lane and lands with the trust plumbing.
return null
}
const hookTarget =
target?.runtime === 'wsl'
? {
+34
View File
@@ -1364,6 +1364,40 @@ describe('registerPtyHandlers', () => {
expect(env.ORCA_CODEX_HOME).toBe(TEST_CODEX_HOME)
})
it('leaves an inherited CODEX_HOME untouched for system default when the flag is OFF', async () => {
// Why: flag OFF must stay byte-identical to today. With no managed home
// selected (resolver null) and the real-home flag off, no CODEX_HOME
// injection or strip happens; an inherited value survives as before.
const env = await spawnAndGetEnv(
undefined,
{ CODEX_HOME: '/tmp/system-codex-home' },
() => null
)
expect(env.CODEX_HOME).toBe('/tmp/system-codex-home')
})
it('strips a nested-Orca override for system default when the real-home flag is ON', async () => {
const env = await spawnAndGetEnv(
{ CODEX_HOME: '/managed/home', ORCA_CODEX_HOME: '/managed/home' },
undefined,
() => null,
() => ({ codexSystemDefaultRealHomeEnabled: true }) as never
)
expect(env.CODEX_HOME).toBeUndefined()
expect(env.ORCA_CODEX_HOME).toBeUndefined()
})
it('preserves a user-owned CODEX_HOME for system default when the real-home flag is ON', async () => {
const env = await spawnAndGetEnv(
{ CODEX_HOME: '/home/me/.config/codex' },
{ ORCA_CODEX_HOME: undefined },
() => null,
() => ({ codexSystemDefaultRealHomeEnabled: true }) as never
)
expect(env.CODEX_HOME).toBe('/home/me/.config/codex')
expect(env.ORCA_CODEX_HOME).toBeUndefined()
})
it('injects explicit proxy settings into local PTY env', async () => {
const env = await spawnAndGetEnv(undefined, undefined, undefined, () => ({
httpProxyUrl: 'http://proxy.example:8080',
+59 -1
View File
@@ -140,6 +140,7 @@ import { setTerminalViewAttributes } from '../runtime/terminal-view-attribute-st
import { validateTerminalViewAttributes } from '../../shared/terminal-view-attributes'
import type { PtyModelRestoreReason } from '../../shared/pty-model-restore-marker'
import type { CodexAccountSelectionTarget } from '../codex-accounts/runtime-selection'
import { isCodexSystemDefaultRealHomeEnabled } from '../codex/codex-real-home-flag'
import { isHostCodexHomeForWsl, isWslCodexHomeForHost } from '../pty/codex-home-wsl-env'
import { buildConfiguredProxyEnv, type NetworkProxySettings } from '../../shared/network-proxy'
import { resolveSetupAgentSequenceLaunchCommand } from '../../shared/setup-agent-sequencing'
@@ -546,6 +547,10 @@ export type BuildPtyHostEnvOptions = {
userDataPath: string
selectedCodexHomePath: string | null
skipCodexHomeEnv?: boolean
/** System-default real-home routing (flag ON): inject no managed CODEX_HOME,
* and strip only an inherited Orca-owned override so nested Orca panes do not
* leak the parent's managed home. A user-set CODEX_HOME is preserved. */
stripInheritedOrcaCodexHome?: boolean
githubAttributionEnabled: boolean
/** The launch command the renderer chose for this PTY (e.g. 'pi', 'omp',
* 'claude'). Used to resolve the per-agent managed extension target for
@@ -617,7 +622,39 @@ function shouldSkipCodexHomeEnvForWindowsShell(
return isWslShellName(shellPath) || (typeof cwd === 'string' && parseWslPath(cwd) !== null)
}
// Why: with the real-home flag ON, a host system-default launch resolves to a
// null managed home. Signal the env builder to strip a nested-Orca-inherited
// override instead of injecting one, so Codex runs on the user's own ~/.codex.
function shouldStripInheritedOrcaCodexHome(args: {
target: CodexAccountSelectionTarget
selectedCodexHomePath: string | null
skipCodexHomeEnv: boolean
settings: GlobalSettings | undefined
}): boolean {
return (
args.target.runtime === 'host' &&
args.selectedCodexHomePath === null &&
!args.skipCodexHomeEnv &&
isCodexSystemDefaultRealHomeEnabled(args.settings)
)
}
const CODEX_HOME_ENV_KEYS = ['CODEX_HOME', 'ORCA_CODEX_HOME'] as const
// Why: system-default real-home routing runs Codex on the user's own ~/.codex.
// Nested Orca panes inherit the parent's Orca-owned override; strip only that
// (CODEX_HOME matching Orca's private ORCA_CODEX_HOME marker), and always drop
// the marker so a shell-ready wrapper cannot restore the managed home. A
// user-set CODEX_HOME with no Orca marker is preserved untouched (see #8606).
function stripInheritedOrcaCodexHomeOverride(baseEnv: Record<string, string>): void {
const inheritedOrcaOverride = baseEnv.ORCA_CODEX_HOME ?? process.env.ORCA_CODEX_HOME
const inheritedCodexHome = baseEnv.CODEX_HOME ?? process.env.CODEX_HOME
if (inheritedOrcaOverride && inheritedCodexHome === inheritedOrcaOverride) {
delete baseEnv.CODEX_HOME
}
delete baseEnv.ORCA_CODEX_HOME
}
type GetSelectedCodexHomePath = (target?: CodexAccountSelectionTarget) => string | null
type PrepareClaudeAuth = (
target?: ClaudeAccountSelectionTarget
@@ -990,6 +1027,8 @@ export function buildPtyHostEnv(
// Why: user startup files may re-export CODEX_HOME; shell-ready wrappers
// restore this runtime home before Codex can be launched from the prompt.
baseEnv.ORCA_CODEX_HOME = opts.selectedCodexHomePath
} else if (opts.stripInheritedOrcaCodexHome) {
stripInheritedOrcaCodexHomeOverride(baseEnv)
}
// Why: WSL shells need the managed userData root for shell-ready wrappers; dev-mode terminals need the same export so `orca` targets the live dev instance.
@@ -1556,11 +1595,18 @@ export function registerPtyHandlers(
codexSelectionTarget,
getSelectedCodexHomePath?.(codexSelectionTarget) ?? null
)
const skipCodexHomeEnv = ctx?.isWsl === true && !selectedCodexHomePath
const env = buildPtyHostEnv(id, baseEnv, {
isPackaged: app.isPackaged,
userDataPath: app.getPath('userData'),
selectedCodexHomePath,
skipCodexHomeEnv: ctx?.isWsl === true && !selectedCodexHomePath,
skipCodexHomeEnv,
stripInheritedOrcaCodexHome: shouldStripInheritedOrcaCodexHome({
target: codexSelectionTarget,
selectedCodexHomePath,
skipCodexHomeEnv,
settings: getSettings?.()
}),
githubAttributionEnabled: getSettings?.()?.enableGitHubAttribution ?? false,
launchCommand: ctx?.command,
launchAgent: ctx?.launchAgent,
@@ -3045,6 +3091,12 @@ export function registerPtyHandlers(
userDataPath: app.getPath('userData'),
selectedCodexHomePath,
skipCodexHomeEnv,
stripInheritedOrcaCodexHome: shouldStripInheritedOrcaCodexHome({
target: codexSelectionTarget,
selectedCodexHomePath,
skipCodexHomeEnv,
settings: getSettings?.()
}),
githubAttributionEnabled: getSettings?.()?.enableGitHubAttribution ?? false,
launchCommand: args.command,
launchAgent: isTuiAgent(args.launchAgent) ? args.launchAgent : undefined,
@@ -3927,6 +3979,12 @@ export function registerPtyHandlers(
userDataPath: app.getPath('userData'),
selectedCodexHomePath,
skipCodexHomeEnv,
stripInheritedOrcaCodexHome: shouldStripInheritedOrcaCodexHome({
target: codexSelectionTarget,
selectedCodexHomePath,
skipCodexHomeEnv,
settings: getSettings?.()
}),
githubAttributionEnabled: getSettings?.()?.enableGitHubAttribution ?? false,
launchCommand: args.command,
launchAgent: isTuiAgent(args.launchAgent) ? args.launchAgent : undefined,
@@ -25,6 +25,20 @@ function cloneProcessEnv(): Record<string, string> {
return env
}
// Why: with system-default real-home routing, the headless Codex commit run
// must use the user's own ~/.codex. If Orca itself was launched from a nested
// Orca terminal it can inherit an Orca-owned CODEX_HOME override; strip only
// that (CODEX_HOME matching the private ORCA_CODEX_HOME marker), preserving a
// user-set CODEX_HOME.
function cloneProcessEnvWithoutOrcaCodexHomeOverride(): Record<string, string> {
const env = cloneProcessEnv()
if (env.ORCA_CODEX_HOME && env.CODEX_HOME === env.ORCA_CODEX_HOME) {
delete env.CODEX_HOME
}
delete env.ORCA_CODEX_HOME
return env
}
function readInheritedOrShellEnvVar(name: string, sourceName?: string): string | undefined {
return (
(sourceName ? process.env[sourceName] : undefined) ??
@@ -100,7 +114,9 @@ export async function prepareLocalCommitMessageAgentEnv(
}
return {
ok: true,
env: codexHomePath ? { ...cloneProcessEnv(), CODEX_HOME: codexHomePath } : undefined
env: codexHomePath
? { ...cloneProcessEnv(), CODEX_HOME: codexHomePath }
: cloneProcessEnvWithoutOrcaCodexHomeOverride()
}
}
+1
View File
@@ -338,6 +338,7 @@ export function getDefaultSettings(homedir: string): GlobalSettings {
minimaxGroupId: '',
minimaxUsageModels: 'general',
geminiCliOAuthEnabled: false,
codexSystemDefaultRealHomeEnabled: false,
agentCmdOverrides: {},
agentDefaultArgs: { ...DEFAULT_TUI_AGENT_ARGS },
agentDefaultEnv: { ...DEFAULT_TUI_AGENT_ENV },
+5
View File
@@ -2841,6 +2841,11 @@ export type GlobalSettings = {
/** Whether to extract OAuth credentials from the local Gemini CLI installation
* for rate-limit fetching. Disabled by default for explicit opt-in. */
geminiCliOAuthEnabled: boolean
/** Staged internal flag (default OFF, no settings UI): route the system-default
* Codex account at the user's real ~/.codex instead of Orca's managed runtime
* home. OFF is byte-identical to today; managed accounts are unaffected.
* See src/main/codex/codex-real-home-flag.ts. */
codexSystemDefaultRealHomeEnabled?: boolean
/** Per-agent CLI command overrides. A missing key means use the catalog default binary name. */
agentCmdOverrides: Partial<Record<TuiAgent, string>>
/** Why: Orca bridges Codex session history from the user's real Codex home into