fix(codex): keep automatic resume home authoritative

This commit is contained in:
Brennan Benson
2026-07-20 23:31:09 -07:00
parent 1a37399cc5
commit 7e5ce8f880
4 changed files with 92 additions and 9 deletions
@@ -37,7 +37,7 @@ describe('resolveTrustedCodexSessionResumeHome', () => {
})
it('rejects paths outside trusted homes or outside the rollout layout', () => {
const fileIsRegular = (): boolean => true
const fileIsRegular = vi.fn((): boolean => true)
expect(
resolveTrustedCodexSessionResumeHome({
transcriptPath: '/tmp/sessions/2026/07/20/rollout-a.jsonl',
@@ -60,6 +60,7 @@ describe('resolveTrustedCodexSessionResumeHome', () => {
fileIsRegular
})
).toBeNull()
expect(fileIsRegular).not.toHaveBeenCalled()
})
it('rejects a trusted-looking path when the rollout no longer exists', () => {
+10 -5
View File
@@ -48,15 +48,20 @@ function resolveTrustedCodexSessionResume(args: {
fileIsRegular?: (filePath: string) => boolean
}): { homePath: string; transcriptPath: string } | null {
const persistedPath = args.transcriptPath?.trim()
const transcriptPath = persistedPath
? resolveExistingRolloutPath(persistedPath, args.fileIsRegular ?? isRegularFile)
: null
if (!transcriptPath) {
if (!persistedPath) {
return null
}
for (const homePath of args.trustedCodexHomes) {
if (isCodexRolloutInsideSessionsRoot(join(homePath, 'sessions'), transcriptPath)) {
const sessionsRoot = join(homePath, 'sessions')
if (!isCodexRolloutInsideSessionsRoot(sessionsRoot, persistedPath)) {
continue
}
const transcriptPath = resolveExistingRolloutPath(
persistedPath,
args.fileIsRegular ?? isRegularFile
)
if (transcriptPath) {
return { homePath, transcriptPath }
}
}
+67 -2
View File
@@ -985,7 +985,8 @@ describe('registerPtyHandlers', () => {
cols: 80,
rows: 24,
command: 'codex resume session-a',
env: { CODEX_HOME: '/custom/codex' },
env: { CODEX_HOME: '/custom/codex', REMOVE_ME: 'stale' },
envToDelete: ['CODEX_HOME', 'ORCA_CODEX_HOME', 'REMOVE_ME'],
launchAgent: 'codex',
resumeProviderSession: {
key: 'session_id',
@@ -998,6 +999,7 @@ describe('registerPtyHandlers', () => {
expect(selectedHome).not.toHaveBeenCalled()
expect(env.CODEX_HOME).toBe(systemHome)
expect(env.ORCA_CODEX_HOME).toBe(systemHome)
expect(env.REMOVE_ME).toBeUndefined()
})
it('does not fall back to the selected account when automatic resume provenance is rejected', async () => {
@@ -1851,7 +1853,8 @@ describe('registerPtyHandlers', () => {
cols: 80,
rows: 24,
command: 'codex resume session-a',
env: { CODEX_HOME: '/custom/codex' },
env: { CODEX_HOME: '/custom/codex', REMOVE_ME: 'stale' },
envToDelete: ['CODEX_HOME', 'ORCA_CODEX_HOME', 'REMOVE_ME'],
launchAgent: 'codex',
resumeProviderSession: {
key: 'session_id',
@@ -1864,6 +1867,68 @@ describe('registerPtyHandlers', () => {
expect(selectedHome).not.toHaveBeenCalled()
expect(env.CODEX_HOME).toBe(systemHome)
expect(env.ORCA_CODEX_HOME).toBe(systemHome)
expect(env.REMOVE_ME).toBeUndefined()
})
it('keeps the authoritative home for runtime-created daemon resumes', async () => {
type RuntimeSpawnController = {
spawn(args: {
cols: number
rows: number
command: string
env: Record<string, string>
envToDelete: string[]
launchAgent: 'codex'
resumeProviderSession: {
key: 'session_id'
id: string
transcriptPath: string
}
}): Promise<{ id: string }>
}
const daemonSpawn = setupDaemonAdapter()
const runtime = {
setPtyController: vi.fn(),
registerPty: vi.fn(),
noteTerminalSpawnCommand: vi.fn(),
onPtySpawned: vi.fn(),
onPtyExit: vi.fn(),
onPtyData: vi.fn()
}
const systemHome = '/Users/example/.codex'
handlers.clear()
registerPtyHandlers(
mainWindow as never,
runtime as never,
undefined,
undefined,
undefined,
undefined,
{ prepareCodexSessionResume: async () => ({ codexHomePath: systemHome }) }
)
const controller = runtime.setPtyController.mock.calls[0]?.[0] as RuntimeSpawnController
await controller.spawn({
cols: 80,
rows: 24,
command: 'codex resume session-a',
env: { CODEX_HOME: '/custom/codex', REMOVE_ME: 'stale' },
envToDelete: ['CODEX_HOME', 'ORCA_CODEX_HOME', 'REMOVE_ME'],
launchAgent: 'codex',
resumeProviderSession: {
key: 'session_id',
id: 'session-a',
transcriptPath: `${systemHome}/sessions/2026/07/20/rollout-a.jsonl`
}
})
const spawnOptions = daemonSpawn.mock.calls.at(-1)?.[0] as DaemonSpawnCall
expect(spawnOptions.env.CODEX_HOME).toBe(systemHome)
expect(spawnOptions.env.ORCA_CODEX_HOME).toBe(systemHome)
expect(spawnOptions.env.REMOVE_ME).toBeUndefined()
expect(spawnOptions.envToDelete ?? []).not.toContain('CODEX_HOME')
expect(spawnOptions.envToDelete ?? []).not.toContain('ORCA_CODEX_HOME')
expect(spawnOptions.envToDelete).toContain('REMOVE_ME')
})
it('prepares Codex project trust before a daemon-backed interactive launch', async () => {
+13 -1
View File
@@ -757,6 +757,12 @@ function mergePtyEnvDeletions(
return Array.from(new Set([...(existingKeys ?? []), ...additionalKeys]))
}
function removeCodexHomeDeletionRequests(keys: string[] | undefined): string[] | undefined {
// Why: resume provenance is launch-authoritative; late deletions must not fall back to the current account.
const filtered = keys?.filter((key) => key !== 'CODEX_HOME' && key !== 'ORCA_CODEX_HOME')
return filtered?.length ? filtered : undefined
}
function getInheritedAgentHookEnvKeysToDelete(
spawnEnv: Record<string, string> | undefined
): string[] {
@@ -2922,6 +2928,9 @@ export function registerPtyHandlers(
'ORCA_CODEX_HOME'
])
}
if (codexResumeHome?.codexHomePath) {
spawnOptions.envToDelete = removeCodexHomeDeletionRequests(spawnOptions.envToDelete)
}
deleteRequestedEnvKeys(env, spawnOptions.envToDelete)
promoteAgentTeamsShimPath(env, requestedAgentTeamsPath)
if (args.command !== undefined) {
@@ -3830,7 +3839,7 @@ export function registerPtyHandlers(
const envToDelete = claudeAuth?.stripAuthEnv
? [...CLAUDE_AUTH_ENV_VARS, 'ANTHROPIC_CUSTOM_HEADERS']
: undefined
const combinedEnvToDelete = mergePtyEnvDeletions(
let combinedEnvToDelete = mergePtyEnvDeletions(
mergePtyEnvDeletions(
mergePtyEnvDeletions(
mergePtyEnvDeletions(
@@ -3845,6 +3854,9 @@ export function registerPtyHandlers(
// main cannot safely decide ownership for a process it may not parent.
stripInheritedOrcaCodexHome ? ['ORCA_CODEX_HOME'] : []
)
if (codexResumeHome?.codexHomePath) {
combinedEnvToDelete = removeCodexHomeDeletionRequests(combinedEnvToDelete)
}
deleteRequestedEnvKeys(spawnEnv, combinedEnvToDelete)
promoteAgentTeamsShimPath(spawnEnv, requestedAgentTeamsPath)
const spawnOptions: PtySpawnOptions = {