From 5a737261d8c5e6a4453a33b0f6712608b074dbf3 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Mon, 28 Sep 2026 22:52:00 -0700 Subject: [PATCH] fix(codex): a real-home resume starts at once, without waiting for approval A resume into the real ~/.codex waited until the background approval settled, up to its 30 s deadline on a cold app-server: bookkeeping for later launches gating the resume the user asked for. It now starts at once. If the approval is still running, that first resume can show Codex's hook review once; the approval then lands and later resumes and plain codex launches are trusted. Trusting Orca's entries per process was the alternative, but the resume command is typed into the pane's shell, and hook settings stay out of typed commands. --- .../codex/codex-real-home-hook-install.ts | 10 -------- .../codex-real-home-slow-app-server.test.ts | 17 ------------- ...odex-launch-during-real-home-grant.test.ts | 25 +++---------------- ...odex-launch-per-agent-hook-opt-out.test.ts | 4 --- .../startup/codex-session-resume-launch.ts | 8 +----- 5 files changed, 5 insertions(+), 59 deletions(-) diff --git a/src/main/codex/codex-real-home-hook-install.ts b/src/main/codex/codex-real-home-hook-install.ts index 2f3de75f265..d968fe7cf86 100644 --- a/src/main/codex/codex-real-home-hook-install.ts +++ b/src/main/codex/codex-real-home-hook-install.ts @@ -116,16 +116,6 @@ export function ensureRealHomeCodexHookState(args: { return ensureInFlight } -/** - * For a resume that must run in the real home, with no managed home to fall - * back to: waits until a background grant settles, which its deadline bounds. - * Settled means Codex approved the entry or the grant withdrew it. - */ -export async function awaitRealHomeCodexHookTrust(): Promise { - await backgroundGrant - return currentLane -} - async function runRealHomeCodexHookEnsure(args: { hooksEnabled: boolean userDataPath: string diff --git a/src/main/codex/codex-real-home-slow-app-server.test.ts b/src/main/codex/codex-real-home-slow-app-server.test.ts index c6f7319c128..15855630e02 100644 --- a/src/main/codex/codex-real-home-slow-app-server.test.ts +++ b/src/main/codex/codex-real-home-slow-app-server.test.ts @@ -34,7 +34,6 @@ vi.mock('../codex-cli/command', () => ({ resolveCodexCommand: resolveCodexComman import { _internals as realHomeInternals, - awaitRealHomeCodexHookTrust, ensureRealHomeCodexHookState, isRealHomeCodexHookLaneUsable } from './codex-real-home-hook-install' @@ -182,22 +181,6 @@ describe('a slow codex app-server start', () => { expect(server.sessions).toBe(1) }) - it('lets a resume into the real home wait until the grant settles', async () => { - const server = installAppServer(15_000) - expect(await launch()).toBe('granting') - - let settled = false - const resumed = awaitRealHomeCodexHookTrust().then((lane) => { - settled = true - return lane - }) - await new Promise((resolve) => setTimeout(resolve, 50)) - expect(settled).toBe(false) - - server.start() - expect(await resumed).toBe('installed') - }) - it('starts no cooldown after a timeout: the next launch tries again at once', async () => { const hung = installAppServer(10 * 60_000) hung.start() diff --git a/src/main/startup/codex-launch-during-real-home-grant.test.ts b/src/main/startup/codex-launch-during-real-home-grant.test.ts index c7b974b912b..91ce89dfd9f 100644 --- a/src/main/startup/codex-launch-during-real-home-grant.test.ts +++ b/src/main/startup/codex-launch-during-real-home-grant.test.ts @@ -73,8 +73,7 @@ vi.mock('./main-process-state', async () => { } }) -const { CODEX_BACKGROUND_TRUST_GRANT_TIMEOUT_MS, _internals: grantInternals } = - await import('../codex/codex-hook-trust-grant') +const { _internals: grantInternals } = await import('../codex/codex-hook-trust-grant') const { _internals: realHomeInternals } = await import('../codex/codex-real-home-hook-install') const { getOrcaManagedCodexHomePath } = await import('../codex/codex-home-paths') const { prepareCodexRuntimeHomeForLaunch } = await import('./codex-launch-preparation') @@ -201,7 +200,7 @@ describe('a Codex launch while the real-home approval hangs', () => { }) describe('a Codex resume into the real ~/.codex while its approval runs', () => { - it('spawns once Codex approves the entry, never beside an unapproved one', async () => { + it('starts at once, and the entry is approved when the grant lands', async () => { let approve: () => void = () => {} const approval = new Promise((resolve) => { approve = resolve @@ -227,27 +226,11 @@ describe('a Codex resume into the real ~/.codex while its approval runs', () => } }) - const resumed = resume() - expect(await settlesWithin(resumed, 200)).toBe(false) + expect(await settlesWithin(resume(), 200)).toBe(true) approve() - await resumed + await realHomeInternals.settledLaneForTesting() const trust = realHomeOrcaEntryTrust() expect(trust.length).toBeGreaterThan(0) expect(trust.every((state) => state === 'trusted')).toBe(true) }) - - it('spawns at the approval deadline with the unapproved entries withdrawn', async () => { - vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date'] }) - grantInternals.setGrantSessionRunner(() => new Promise(() => {})) - let spawned = false - const resumed = resume().then(() => { - spawned = true - }) - - await vi.advanceTimersByTimeAsync(CODEX_BACKGROUND_TRUST_GRANT_TIMEOUT_MS - 1) - expect(spawned).toBe(false) - await vi.advanceTimersByTimeAsync(1) - await resumed - expect(realHomeOrcaEntryTrust()).toEqual([]) - }) }) diff --git a/src/main/startup/codex-launch-per-agent-hook-opt-out.test.ts b/src/main/startup/codex-launch-per-agent-hook-opt-out.test.ts index 721965918e2..cc7d36cefb6 100644 --- a/src/main/startup/codex-launch-per-agent-hook-opt-out.test.ts +++ b/src/main/startup/codex-launch-per-agent-hook-opt-out.test.ts @@ -20,7 +20,6 @@ const mocks = vi.hoisted(() => { installForLaunchPrep: vi.fn(async () => {}), refreshRuntimeUserHooksForLaunchPrep: vi.fn(async () => {}), ensureRealHomeCodexHookState: vi.fn(async () => 'installed' as const), - awaitRealHomeCodexHookTrust: vi.fn(async () => 'installed' as const), prepareCodexSessionResume: vi.fn() } }) @@ -35,7 +34,6 @@ vi.mock('../codex/hook-service', () => ({ } })) vi.mock('../codex/codex-real-home-hook-install', () => ({ - awaitRealHomeCodexHookTrust: mocks.awaitRealHomeCodexHookTrust, ensureRealHomeCodexHookState: mocks.ensureRealHomeCodexHookState })) // Why: the real predicate, without loading every agent's hook service. @@ -166,8 +164,6 @@ describe('Codex launch prep honours the per-agent hook opt-out', () => { expect(mocks.ensureRealHomeCodexHookState).toHaveBeenCalledWith( expect.objectContaining({ hooksEnabled: codexHooksOn, writePolicy: 'add-missing-only' }) ) - // Why: a resume has no managed home to fall back to, so it waits for the grant to settle. - expect(mocks.awaitRealHomeCodexHookTrust).toHaveBeenCalledOnce() expect(mocks.installForLaunchPrep).not.toHaveBeenCalled() expect(mocks.refreshRuntimeUserHooksForLaunchPrep).not.toHaveBeenCalled() } diff --git a/src/main/startup/codex-session-resume-launch.ts b/src/main/startup/codex-session-resume-launch.ts index c554e184310..fef07c94dc2 100644 --- a/src/main/startup/codex-session-resume-launch.ts +++ b/src/main/startup/codex-session-resume-launch.ts @@ -6,10 +6,7 @@ import { prepareCodexSessionResume } from '../codex/codex-session-resume-prepara import { prepareLegacySharedCodexSessionResume } from '../codex/codex-legacy-session-resume' import { ManagedCodexHomeTemporarilyUnavailableError } from '../codex-accounts/host-codex-managed-home-ownership' import { codexHookService } from '../codex/hook-service' -import { - awaitRealHomeCodexHookTrust, - ensureRealHomeCodexHookState -} from '../codex/codex-real-home-hook-install' +import { ensureRealHomeCodexHookState } from '../codex/codex-real-home-hook-install' import { isAgentStatusHooksEnabledForAgent } from '../agent-hooks/managed-agent-hook-controls' import { markCodexProjectTrusted } from '../agent-trust-presets' import { awaitAgentTrustWriteWithinDeadline } from '../agent-trust-write-deadline' @@ -106,9 +103,6 @@ export async function prepareCodexSessionResumeForLaunch(args: { userDataPath: app.getPath('userData'), writePolicy: 'add-missing-only' }) - // Why wait: an unapproved entry would show hook review in this pane, and - // the grant's own settle is the only one that cannot race Codex's write. - await awaitRealHomeCodexHookTrust() } else if (hooksEnabled) { await codexHookService.installForLaunchPrep(resumeHome) } else {