diff --git a/src/main/agent-awake-service-platform-assertions.test.ts b/src/main/agent-awake-service-platform-assertions.test.ts index 9c08064abfc..cd1d7fb1adc 100644 --- a/src/main/agent-awake-service-platform-assertions.test.ts +++ b/src/main/agent-awake-service-platform-assertions.test.ts @@ -49,13 +49,15 @@ function createPlatformAssertion() { function createService( blocker = createBlocker(), macosAssertion = createPlatformAssertion(), - linuxAssertion = createPlatformAssertion() + linuxAssertion = createPlatformAssertion(), + platform: NodeJS.Platform = 'linux' ): AgentAwakeService { return new AgentAwakeService({ blocker, linuxAssertion, macosAssertion, now: () => 1_000, + platform, powerMonitor: null, logger: { debug: vi.fn(), @@ -65,6 +67,18 @@ function createService( } describe('AgentAwakeService platform assertions', () => { + it('uses caffeinate without Electron display blocking on macOS', () => { + const blocker = createBlocker() + const macosAssertion = createPlatformAssertion() + const service = createService(blocker, macosAssertion, createPlatformAssertion(), 'darwin') + + service.setEnabled(true) + service.setStatuses([workingStatus()]) + + expect(macosAssertion.start).toHaveBeenCalledTimes(1) + expect(blocker.start).not.toHaveBeenCalled() + }) + it('keeps Electron blocker active when macOS assertion start fails', () => { const blocker = createBlocker() const macosAssertion = createPlatformAssertion() @@ -72,7 +86,7 @@ describe('AgentAwakeService platform assertions', () => { macosAssertion.start.mockImplementation(() => { throw new Error('caffeinate failed') }) - const service = createService(blocker, macosAssertion, linuxAssertion) + const service = createService(blocker, macosAssertion, linuxAssertion, 'darwin') service.setEnabled(true) service.setStatuses([workingStatus()]) @@ -85,6 +99,20 @@ describe('AgentAwakeService platform assertions', () => { expect(linuxAssertion.stop).toHaveBeenCalled() }) + it('drops the display-blocking fallback after caffeinate recovers', () => { + const blocker = createBlocker() + const macosAssertion = createPlatformAssertion() + macosAssertion.start.mockImplementationOnce(() => false).mockImplementation(() => true) + const service = createService(blocker, macosAssertion, createPlatformAssertion(), 'darwin') + + service.setEnabled(true) + service.setStatuses([workingStatus()]) + expect(blocker.start).toHaveBeenCalledWith('prevent-display-sleep') + + service.setStatuses([{ ...workingStatus(), receivedAt: 1_001 }]) + expect(blocker.stop).toHaveBeenCalledWith(1) + }) + it('keeps Electron blocker active when Linux assertion start fails', () => { const blocker = createBlocker() const macosAssertion = createPlatformAssertion() diff --git a/src/main/agent-awake-service.test.ts b/src/main/agent-awake-service.test.ts index 6a85e45c41a..d1792e665fd 100644 --- a/src/main/agent-awake-service.test.ts +++ b/src/main/agent-awake-service.test.ts @@ -85,6 +85,7 @@ function createService( linuxAssertion, macosAssertion, now, + platform: 'linux', powerMonitor, logger: { debug: vi.fn(), diff --git a/src/main/agent-awake-service.ts b/src/main/agent-awake-service.ts index adba93e7ea1..29e866d27f2 100644 --- a/src/main/agent-awake-service.ts +++ b/src/main/agent-awake-service.ts @@ -23,7 +23,7 @@ type PowerSaveBlocker = { } type PlatformAwakeAssertion = { - start: (reason: string) => void + start: (reason: string) => boolean | void stop: (reason: string) => void dispose: () => void } @@ -41,6 +41,7 @@ type AgentAwakeServiceOptions = { logger?: Logger macosAssertion?: PlatformAwakeAssertion now?: () => number + platform?: NodeJS.Platform powerMonitor?: PowerMonitorEventSource | null } @@ -55,6 +56,7 @@ export class AgentAwakeService { private readonly linuxAssertion: PlatformAwakeAssertion private readonly logger: Logger private readonly macosAssertion: PlatformAwakeAssertion + private readonly platform: NodeJS.Platform private readonly now: () => number private readonly unsubscribeResume: (() => void) | null @@ -78,6 +80,7 @@ export class AgentAwakeService { now: this.now, onUnexpectedFailure: (reason) => this.refresh(reason) }) + this.platform = options.platform ?? process.platform const resumeSource = options.powerMonitor === undefined ? powerMonitor : options.powerMonitor if (resumeSource) { const onResume = () => this.refresh('power-resume') @@ -132,8 +135,12 @@ export class AgentAwakeService { const runningStatusCount = this.getEligibleRunningStatusCount() const shouldBlock = this.mode === 'on' || (this.mode === 'auto' && runningStatusCount > 0) if (shouldBlock) { - this.startBlocker(reason, runningStatusCount) - this.startMacosAssertion(reason) + const macosAssertionActive = this.startMacosAssertion(reason) + if (this.platform !== 'darwin' || !macosAssertionActive) { + this.startBlocker(reason, runningStatusCount) + } else { + this.stopBlocker('macos-assertion-active', runningStatusCount) + } this.startLinuxAssertion(reason) } else { this.stopBlocker(reason, runningStatusCount) @@ -229,15 +236,16 @@ export class AgentAwakeService { } } - private startMacosAssertion(reason: string): void { + private startMacosAssertion(reason: string): boolean { try { - this.macosAssertion.start(reason) + return this.macosAssertion.start(reason) !== false } catch (err) { this.logger.warn('[agent-awake] failed to start macOS system sleep assertion', { reason, mode: this.mode, error: err }) + return false } } diff --git a/src/main/macos-system-sleep-assertion.ts b/src/main/macos-system-sleep-assertion.ts index 67a7567b138..84250eb77af 100644 --- a/src/main/macos-system-sleep-assertion.ts +++ b/src/main/macos-system-sleep-assertion.ts @@ -53,13 +53,16 @@ export class MacosSystemSleepAssertion { this.spawn = options.spawn ?? nodeSpawn } - start(reason: string): void { - if (this.platform !== 'darwin' || this.child) { - return + start(reason: string): boolean { + if (this.platform !== 'darwin') { + return false + } + if (this.child) { + return true } if (this.retryNotBefore !== null && this.now() < this.retryNotBefore) { this.scheduleRetry() - return + return false } let child: CaffeinateProcess @@ -70,7 +73,7 @@ export class MacosSystemSleepAssertion { }) } catch (error) { this.handleFailure('spawn-error', reason, error) - return + return false } this.child = child @@ -91,6 +94,7 @@ export class MacosSystemSleepAssertion { child.on('exit', onExit) this.resetRetrySuppression() this.resetFailureStreak() + return true } stop(_reason: string): void {