ITADN

Feature request: optional "Stay Awake" : hold a power assertion while an agent session is live

#2740Openkocaemre 创建于 14 天前
P3issue-rating: 🌊 off-meta tidepoolclawsweeper:no-new-fix-prclawsweeper:needs-maintainer-reviewclawsweeper:needs-product-decisionimpact:ux-friction
K
kocaemrecommented
## Problem A lot of people now drive Claude Code / Codex from their phone (Claude Code remote control, Codex web/app, `tmux` + SSH, etc.). The workflow is: kick off a task, walk away, check back from the phone, send the next prompt. On a MacBook this breaks in a specific and annoying way: the agent finishes its task, the machine goes idle, macOS puts it to sleep, and the next prompt from the phone never lands. The session is technically alive but the Mac isn't. You come back an hour later to a queued prompt that never ran. Today the workaround is a second tool — `caffeinate -dimsu` in a stray terminal tab, or another menu bar app (KeepingYouAwake, StayAwake, cc-caffeine, agents-sleep-preventer, …). That means two menu bar icons doing overlapping jobs, and every one of those tools has the same weakness: **they don't know whether an agent is actually running.** They're either always-on (battery burn) or manually toggled (you forget). ## Why CodexBar is the right place for this CodexBar already solves the hard half. Per `docs/agent-sessions-design.md`, `LocalAgentSessionScanner` does process-backed discovery of live Codex / Claude Code / Pi sessions via libproc — explicitly *not* "a transcript file exists" but "the process is running." That's exactly the signal a sleep-prevention feature needs, and it's the signal every standalone tool lacks. There's also precedent for touching power APIs here: `CodexCostCatchUpPolicy.swift` already imports `IOKit.ps` and reads `IOPSGetProvidingPowerSourceType` to throttle work on battery. So the feature is small: take the live-session signal that already exists, and hold a power assertion while it's true. ## Proposal A new setting, **off by default**. **Modes** - `Off` (default) - `While an agent session is live` — assertion held while `LocalAgentSessionScanner` reports ≥1 live session, released after a configurable grace period (default ~5 min) once the last one exits - `While CodexBar is running` — plain always-on, for people who just want a caffeinate replacement - `Timed` — 30 min / 1 h / 2 h / until a chosen time **Sub-options** - `Also prevent display sleep` — off by default; system-sleep prevention alone is enough for remote control and much gentler on battery - `Only while on AC power` — on by default, reusing `CodexCostCatchUpPowerSource.current()` **Feedback** Some visible indication while the assertion is held (a small badge on the menu bar icon, or a line in the menu saying what's holding it awake and why). Silent, invisible battery drain is the main risk of this feature, so it should never be a mystery that it's on. ## Implementation sketch ```swift IOPMAssertionCreateWithName( kIOPMAssertPreventUserIdleSystemSleep as CFString, IOPMAssertionLevel(kIOPMAssertionLevelOn), "CodexBar: agent session active" as CFString, &assertionID ) ``` - One assertion at a time, owned by a small `@Observable` manager, released on mode change / session end / app termination. - Crash-safe by construction: assertions are per-process and the kernel drops them when the process dies, so there's no "stuck awake forever" failure mode. - `kIOPMAssertPreventUserIdleDisplaySleep` only when the display sub-option is on. - Testable without AppKit: the manager's decision logic (sessions + power source + mode → should hold assertion) can be a pure function covered by focused tests, per the testing guidance in AGENTS.md. ## Naming Not "Session Keepalive" — `docs/session-keepalive-design.md` already uses that for auth token / cookie refresh, and conflating the two would be confusing. "Stay Awake" or "Prevent Sleep" reads unambiguously. ## Non-goals / known limitation `IOPMAssertion` does **not** prevent sleep when the lid is closed (clamshell without an external display) on Apple Silicon. Covering that requires `pmset -b disablesleep 1`, which needs root — a scoped sudoers rule or a privileged helper. That seems clearly out of scope for CodexBar and I'm not proposing it. The setting's help text should just say so plainly: lid open (or external display connected) required. Also out of scope: waking a sleeping Mac, Wake-on-LAN, and anything network-side. ## Happy to send a PR If you're open to the feature, I'd like to implement it — assertion manager, settings UI, tests, docs page — following the existing conventions (`swiftformat` / `swiftlint --strict`, `make check`, `make test`). I'd rather check the appetite and agree on the shape (especially default-off, the AC-power guard, and the naming) before writing code. If you'd prefer a narrower v1 — say, only the `While an agent session is live` mode and nothing else — that works too. Thanks Emre
1 条评论