Skip to content

feat(desktop): add keep awake while agents are active setting#484

Merged
wesbillman merged 1 commit intomainfrom
prevent-sleep
May 5, 2026
Merged

feat(desktop): add keep awake while agents are active setting#484
wesbillman merged 1 commit intomainfrom
prevent-sleep

Conversation

@wesbillman
Copy link
Copy Markdown
Collaborator

Summary

  • Adds a "Keep awake while agents are active" toggle to Settings > Agents that holds a macOS PreventUserIdleSystemSleep IOKit power assertion while local managed agents are running
  • Safety rails: auto-release when agents stop, 4-hour hard cap with user notification, cleanup on app exit
  • Toggle defaults to OFF; no-op on non-macOS platforms

Implementation

Rust backend (prevent_sleep.rs): Raw IOKit FFI — IOPMAssertionCreateWithName / IOPMAssertionRelease behind #[cfg(target_os = "macos")]. State managed via Arc<Mutex<PreventSleepState>>. 4-hour timer auto-releases and emits a Tauri event.

Frontend (usePreventSleep.ts): React context provider (single source of truth) watches managed agent status and syncs to backend. Settings card consumes context — no dual-hook race.

Safety design:

  • PreventUserIdleSystemSleep (not display sleep) — only prevents idle sleep, lid-close/critical-battery/manual sleep still work
  • Assertion tied to status === "running" agents only (not "deployed" remote agents)
  • 4-hour hard cap: timer calls release() then emits frontend notification
  • RunEvent::Exit handler releases before shutdown_managed_agents
  • CFString null checks in unsafe block

Files changed (11)

  • 4 new: prevent_sleep.rs, commands/prevent_sleep.rs, usePreventSleep.ts, PreventSleepSettingsCard.tsx
  • 7 modified: app_state.rs, commands/mod.rs, lib.rs, AppShell.tsx, SettingsPanels.tsx, tauri.ts, check-file-sizes.mjs

Test plan

  • Toggle ON with no agents running → status shows "Waiting for agents to start", pmset -g assertions shows no Sprout assertion
  • Start a local agent → status shows "Active", pmset -g assertions shows PreventUserIdleSystemSleep by Sprout
  • Stop all agents → assertion auto-releases, status shows "Inactive"
  • Toggle OFF while agents running → assertion releases immediately
  • Wait 4 hours (or reduce CAP_SECONDS for testing) → assertion releases, expiry banner appears
  • Quit app while assertion held → assertion released (verify via pmset -g assertions)
  • Settings toggle persists across app restart (localStorage)

🤖 Generated with Claude Code

Prevents macOS idle sleep while local managed agents are running via an
IOKit PreventUserIdleSystemSleep power assertion. Safety rails: auto-release
when agents stop, 4-hour hard cap with frontend notification, and cleanup
on app exit. Toggle defaults to OFF. No-op on non-macOS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@wesbillman wesbillman merged commit ec489c5 into main May 5, 2026
14 checks passed
@wesbillman wesbillman deleted the prevent-sleep branch May 5, 2026 20:14
wesbillman added a commit that referenced this pull request May 5, 2026
…ter expiry

Two fixes from Codex adversarial review of #484:

1. Replace tokio::spawn with tauri::async_runtime::spawn for the 4-hour
   cap timer. Follows the codebase convention for spawning from non-async
   contexts (see huddle/models.rs:381 rationale).

2. Include !expired in the active derivation so the status badge shows
   "Inactive" after the 4-hour cap fires, matching the actual backend
   state where the IOKit assertion has been released.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant