From 0146ba170bd4ee0992cc9cde689d57ed8a4f3a05 Mon Sep 17 00:00:00 2001 From: Neil Date: Thu, 22 Oct 2026 05:11:09 -0700 Subject: [PATCH] fix(agent-hooks): a WSL host's Codex account-home launch with hooks off skips too; mark the R42 guard to drop with the bridge Co-Authored-By: Claude --- .../orcad/orcad-codex-account-homes.test.ts | 15 +++++++++++++++ src/main/orcad/orcad-codex-account-homes.ts | 11 ++++++----- ...ime-client-settings-wsl-host-hooks.test.ts | 19 +++++++++++++++++-- src/main/runtime/runtime-client-settings.ts | 2 +- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/orcad/orcad-codex-account-homes.test.ts b/src/main/orcad/orcad-codex-account-homes.test.ts index 79975aefd73..92010e43be1 100644 --- a/src/main/orcad/orcad-codex-account-homes.test.ts +++ b/src/main/orcad/orcad-codex-account-homes.test.ts @@ -12,6 +12,8 @@ const { prepareHooks, resolveHome, listHomes } = vi.hoisted(() => ({ listHomes: vi.fn(() => ['/home/u/.local/share/orca/codex-accounts/a1/home']) })) +const wslHost = vi.hoisted(() => vi.fn(() => false)) +vi.mock('../ipc/wsl-guest-windows-drive-path', () => ({ isWslHostOrcadProcess: wslHost })) vi.mock('../codex/codex-account-home-hook', () => ({ reconcileCodexAccountHomeHook: prepareHooks })) @@ -41,6 +43,19 @@ describe('orcadCodexAccountHomes', () => { prepareHooks.mockClear() }) + it('on a WSL host with hooks off, leaves the bridge-shared entry alone (R42)', async () => { + wslHost.mockReturnValue(true) + const homes = orcadCodexAccountHomes( + () => settings({ agentStatusHooksEnabled: false }), + noCleanup + ) + await expect(homes.selectLaunchHome(undefined, { CODEX_HOME: ACCOUNT_HOME })).resolves.toBe( + ACCOUNT_HOME + ) + expect(prepareHooks).not.toHaveBeenCalled() + wslHost.mockReturnValue(false) + }) + it('puts the hook into a client-sent account home and launches on it', async () => { const homes = orcadCodexAccountHomes(() => settings({}), noCleanup) await expect( diff --git a/src/main/orcad/orcad-codex-account-homes.ts b/src/main/orcad/orcad-codex-account-homes.ts index 8ef17653529..5ed12cbc837 100644 --- a/src/main/orcad/orcad-codex-account-homes.ts +++ b/src/main/orcad/orcad-codex-account-homes.ts @@ -7,6 +7,7 @@ import { import { reconcileCodexAccountHomeHook } from '../codex/codex-account-home-hook' import { stopCodexStateDbBackfillRecoveries } from '../codex/codex-state-db-backfill-recovery' import type { GetSelectedCodexHomePath } from '../ipc/pty/host-env/types' +import { isWslHostOrcadProcess } from '../ipc/wsl-guest-windows-drive-path' import type { OrcadRuntimeCleanup } from './orcad-runtime-lifetime' /** @@ -26,11 +27,11 @@ export function orcadCodexAccountHomes( if (!home) { return null } - await reconcileCodexAccountHomeHook( - home, - () => isAgentStatusHooksEnabledForAgent(getSettings(), 'codex'), - context?.launchesCodex === true - ) + const isEnabled = () => isAgentStatusHooksEnabledForAgent(getSettings(), 'codex') + // R42, COMPAT(wsl-bridge): the bridge's panes share these homes; off skips until D4 drops it. + if (isEnabled() || !isWslHostOrcadProcess()) { + await reconcileCodexAccountHomeHook(home, isEnabled, context?.launchesCodex === true) + } return home } return { diff --git a/src/main/runtime/runtime-client-settings-wsl-host-hooks.test.ts b/src/main/runtime/runtime-client-settings-wsl-host-hooks.test.ts index 2d0c6bda396..74ea761a055 100644 --- a/src/main/runtime/runtime-client-settings-wsl-host-hooks.test.ts +++ b/src/main/runtime/runtime-client-settings-wsl-host-hooks.test.ts @@ -19,10 +19,13 @@ vi.mock('../ipc/wsl-guest-windows-drive-path', () => ({ })) function controller(): RuntimeClientSettingsController { - const settings = createGlobalSettingsFixture({ agentStatusHooksEnabled: true }) + let settings = createGlobalSettingsFixture({ agentStatusHooksEnabled: true }) return new RuntimeClientSettingsController({ getSettings: () => settings, - updateSettings: (updates: Partial) => Object.assign(settings, updates), + updateSettings: (updates: Partial) => { + settings = { ...settings, ...updates } + return settings + }, runDurableMutation: async (mutate) => mutate().value }) } @@ -46,4 +49,16 @@ describe('turning status hooks off through a paired client', () => { expect.objectContaining({ keepSharedEntries: keep }) ) }) + + it('keeps them too when one agent is disabled on a WSL host', async () => { + mocks.wslHost.mockReturnValue(true) + + await controller().update({ disabledTuiAgents: ['claude'] }) + + expect(mocks.apply).toHaveBeenCalledWith( + true, + expect.anything(), + expect.objectContaining({ keepSharedEntries: true }) + ) + }) }) diff --git a/src/main/runtime/runtime-client-settings.ts b/src/main/runtime/runtime-client-settings.ts index 5165c52976a..33ca630fb96 100644 --- a/src/main/runtime/runtime-client-settings.ts +++ b/src/main/runtime/runtime-client-settings.ts @@ -246,7 +246,7 @@ export class RuntimeClientSettingsController { return } await applyAgentStatusHooksEnabled(settings.agentStatusHooksEnabled !== false, settings, { - // R42: in a distro the WSL bridge still runs panes on these entries, as startup knows. + // R42, COMPAT(wsl-bridge): the bridge's panes in this distro share these entries; drop at D4. keepSharedEntries: isWslHostOrcadProcess(), shouldHydrateShellPath: getAppEnvironment().isPackaged(), onInstallError: recordManagedHookInstallFailure,