Imported from airtaxi/LidGuard (
.codex/skills/lidguard-power-runtime/SKILL.md). Install upstream withnpx skills add airtaxi/LidGuard --skill lidguard-power-runtime. Copyright stays with the author.
LidGuard Power Runtime
Platform Power Control
Windows
- Use
PowerCreateRequest,PowerSetRequest, andPowerClearRequestfor normal idle sleep prevention. - Use
PowerRequestSystemRequiredto prevent idle system sleep. - Use
PowerRequestAwayModeRequiredto request away-mode behavior where supported. - Keep
PowerRequestDisplayRequiredoptional; use it only when display sleep should also be prevented. - Always clear power requests and close handles when protection ends.
- Do not change sleep idle timeouts. Runtime crashes could leave the user's system policy in a dangerous state.
Linux
- Target systemd/logind environments.
- Use
systemd-inhibitblock inhibitors for normal sleep and idle prevention. - Map
PreventSystemSleepto thesleepinhibitor. - Map
PreventSystemSleepandPreventDisplaySleepto theidleinhibitor. - Keep
PreventAwayModeSleepWindows-only; do not expose or apply it in Linux settings/help output. - Map temporary lid-close protection to a separate
handle-lid-switchinhibitor only whenChangeLidActionis enabled. - Keep
SystemdInhibitorreleasing normally by closing stdin on the helper process; use process-tree kill only as fallback. - Tie inhibitor helper processes to the LidGuard runtime lifetime so stale orphaned inhibitors do not survive the runtime.
- Request immediate sleep/hibernate with
systemctl suspendorsystemctl hibernate, returning exit code/stderr when the request fails. - Keep Linux as a supported top-level platform when
OperatingSystem.IsLinux()is true, even when systemd/logind prerequisites are incomplete; report missing prerequisites from the specific runtime or diagnostic operation. - Do not depend on a long-lived
sudo -vcache. Prepare persistent privileged logind actions through the Linux polkit rule command.
macOS
- Target local macOS systems with
caffeinate,pmset,ioreg,system_profiler, Apple SiliconIOHIDEventSystemClienttemperature sensors, and best-effortpowermetrics. - Allow the Apple Silicon temperature fast path to use direct source-generated
LibraryImportcalls into CoreFoundation and IOKit, plusNativeLibraryexport lookup for CoreFoundation callback tables, because CsWin32 does not cover macOS frameworks. - Use
caffeinateassertions for normal idle sleep prevention. - Map
PreventSystemSleeptocaffeinate -i. - Map
PreventDisplaySleeptocaffeinate -d. - Do not use
caffeinate -sfor LidGuard's cross-platformPreventAwayModeSleepsetting. Away-mode sleep prevention is Windows-only, and macOS sleep prevention must rely oncaffeinate -iso it works on AC and DC power. - Map temporary lid-close protection to
pmset -a disablesleep 1only whenChangeLidActionis enabled. - Before changing
SleepDisabled, save the original state through the pending lid-action backup JSON path; restore it when protection ends or during the next CLI recovery path before normal command execution. - Request immediate Sleep with
pmset sleepnow. - For Hibernate, temporarily back up the current supported
hibernatemode, writehibernatemode 25, requestpmset sleepnow, and leave the pending hibernatemode backup for the next CLI recovery path instead of restoring it immediately afterpmset sleepnowreturns. Ifpmset sleepnowfails, roll back the hibernatemode immediately when possible. - Use non-interactive
sudo -nonly for the exact commands allowed bymacos-permission install; fail with actionable setup guidance instead of prompting in background runtime paths.
Lid Close Policy
- Windows lid close behavior is power setting
LIDACTION. - Use subgroup GUID
4f971e89-eebd-4455-a8de-9e59040e7347. - Use setting GUID
5ca83367-6e45-459f-a27b-476b1d01c936. - Interpret values as
0 = Do Nothing,1 = Sleep,2 = Hibernate,3 = Shut Down. - Read AC/DC values from the active power scheme together before making changes.
- During active sessions, write AC/DC values to
0 = Do Nothingwhen the setting is enabled. - After the last active session stops, restore the backed-up AC/DC values.
- Restore the scheme that was active at backup time.
- Treat the pending lid-action backup JSON as the authoritative restore source. If it already exists, do not overwrite it with a new capture, and restore from that JSON instead of any in-memory value. If restore is expected but the JSON is missing, skip lid policy writes and append a failure entry to the runtime session log.
- On Linux,
ChangeLidAction=truemeans LidGuard holds a systemd/logindhandle-lid-switchinhibitor while protection is applied. Do not edit distribution power configuration files. - On macOS,
ChangeLidAction=truemeans LidGuard temporarily appliespmset -a disablesleep 1, records the originalSleepDisabledvalue in pending backup state, and restores it after protection ends or during crash recovery.
Lid State And Suspend
- Windows lid open/close notification uses
GUID_LIDSWITCH_STATE_CHANGE. - Interpret broadcast values as
0x0 = lid closedand0x1 = lid opened. - Convert lid broadcast values through
LidSwitchNotificationRegistrationtoLidSwitchState. - Start Windows closed-lid decisions from
GetSystemMetrics(SM_CMONITORS)and exclude inactive monitor connections reported by WMI. - For final Windows suspend eligibility while
LidSwitchStateisClosed, also exclude internal laptop panel connections and treat the machine as suspend-eligible only when visible display monitor count is0. - Request immediate Windows sleep/hibernate with
SetSuspendStateafter enablingSeShutdownPrivilege. - Expect
SetSuspendState(false, ...)to fail withERROR_NOT_SUPPORTEDon some Modern Standby systems. - Read Linux lid state from
/proc/acpi/button/lid/*/state; reportUnknownwhen no readable state exists. - Count Linux visible display monitors from
/sys/class/drm/*/status; for final suspend eligibility while the lid is closed, exclude internal connector families such aseDP,LVDS, andDSI. - Request Linux immediate sleep/hibernate with
systemctl suspendorsystemctl hibernate. - Read macOS lid state from
ioregclamshell state; reportUnknownwhen no readable state exists. - Count macOS visible display monitors from
system_profiler SPDisplaysDataType -json; for final suspend eligibility while the lid is closed, exclude built-in/internal display entries. - Request macOS immediate sleep with
pmset sleepnow; request hibernate with the temporaryhibernatemode 25flow. - After the last active session stops, request suspend after the configured post-stop delay using the configured suspend mode only when the lid is closed and suspend eligibility visible display monitor count is
0. - Treat a delay of
0as immediate suspend. - When
closedLidStopFollowUpWebhookUrlis valid,closedLidStopFollowUpDelaySeconds >= 20, andpostStopSuspendDelaySeconds >= 10, supported Stop integrations wait through the post-stop safety delay first, then start the ask-before-sleep reply webhook. Blocking-capable provider hooks keep the Stop hook open during that wait; OpenCode simulates the same flow aftersession.idleand reinjects a reply withclient.session.prompt().postStopSuspendDelaySecondsprotects immediately-following prompts from being skipped too early;closedLidStopFollowUpDelaySecondsis the reply window after the notification is sent. A reply cancels the pending suspend and resumes the session, while no reply falls through to the existing suspend flow. If the user cancels the reply wait from the notification UI/API, reschedule the same pending suspend with a0second delay soPreSuspend, post-stop sound, final re-check, and the real suspend request start immediately. - When a continued Stop arrives with
stop_hook_active = trueor OpenCode's simulatedstopHookActive = trueandRepeatClosedLidStopFollowUp = false, skip both the repeated ask-before-sleep reply wait and the normal post-stop suspend delay; proceed as an immediate suspend attempt after the usual closed-lid/display checks. - Keep normal LidGuard protection active during the pending suspend delay, the ask-before-sleep reply wait, the
PreSuspendwebhook, and post-stop sound playback. Release protection only immediately before the actual suspend request. If pending suspend is canceled, release protection only when no active or resumed session still needs it. - If an ask-before-sleep reply wait sound is configured, play it once after the StopFollowUp webhook start succeeds and the returned poll URL is validated, before the polling loop begins. Sound or volume override failures must be logged and must not interrupt reply waiting or the suspend flow.
- If an ask-before-sleep reply wait sound volume override is configured, use the same default output device master volume capture, temporary unmute/volume set, and restore cleanup rules used for post-stop suspend sounds.
- If a post-stop suspend sound is configured, wait for the delay first, send the pre-suspend webhook when configured, play the configured sound to completion, then re-check lid/session state before requesting suspend.
- If a post-stop suspend sound volume override is configured, capture the default output device master volume and mute state immediately before playback, temporarily unmute as needed, set the configured master volume percent for playback, then restore the previous volume and mute state in the sound playback cleanup path.
- If a pre-suspend webhook URL is configured, POST JSON with a 5-second timeout after the post-stop suspend delay and before post-stop suspend sound playback.
- Require pre-suspend webhook bodies to include
eventType = PreSuspendandreason; include soft-locked session count for soft-lock-triggered suspend. - When provider-reported normal session end schedules suspend, include the same provider/session identity, UTC timestamps, end reason metadata, active session count, working directory, transcript path,
inputPromptPreview, andlastAssistantMessagefields in thePreSuspendbody when available. - If pending suspend is canceled before the pre-suspend webhook is attempted, allow the suppressed normal session-end notification to fall back to
PostSessionEndwhen configured. - Require notification receivers to reject webhook payloads that omit
eventType. - Windows-only
SkipSuspendWhenLidCloseDoesNothing(default true): before the post-stop or soft-lock suspend procedure, evaluate the user's original active lid close action for the current power connection. When a temporary lid action policy is applied, read the original AC/DC values from the pending lid-action backup instead of the live power scheme, because the live scheme currently reads the temporary Do Nothing write. Determine the current power line withGetSystemPowerStatus; mapACLineStatus0 to DirectCurrent, 1 or 2 to AlternatingCurrent, and treat an unknown status as a failure that logs and proceeds without skipping. When the original action is Do Nothing, skip the whole suspend procedure (delay, sound, webhooks, stop follow-up wait, and suspend request) with only session log entries, releasing protection through the normal not-applicable path. Read failures log a runtime warning and do not skip. Emergency Hibernation is not affected.
Emergency Hibernation
- Use
SystemThermalInformation.GetSystemTemperatureCelsius(EmergencyHibernationTemperatureMode)to read the selected available system thermal-zone temperature in Celsius. - On Linux, read millidegree Celsius values from
/sys/class/thermal/thermal_zone*/tempand apply the configured Low, Average, or High aggregation. - On macOS, try Apple Silicon
IOHIDEventSystemClientprocessor temperature sensors first, then fall back topowermetrics --samplers smcCelsius sensor output, applying Low, Average, or High aggregation. - Treat unsupported sensors, permission failures, unsupported samplers, missing numeric Celsius values, and timeouts as unavailable; do not trigger Emergency Hibernation from unavailable data.
- Keep Emergency Hibernation temperature mode configurable as Low, Average, or High, defaulting to Average.
- Run the thermal monitor only while shared keep-awake protection is applied, the lid is closed, and suspend eligibility visible display monitor count is
0. - Poll every 10 seconds.
- Keep the Emergency Hibernation threshold configurable, defaulting to 93 Celsius, and always clamp it to 70 through 110 Celsius before runtime use.
- When observed temperature reaches the clamped threshold, cancel any pending post-stop suspend, send the pre-suspend webhook with
reason = EmergencyHibernationusing a 5-second timeout, then immediately request hibernate. - If the hibernate request fails, immediately request Sleep as a best-effort fallback and record both suspend results.
- Ignore the regular suspend mode, post-stop suspend delay, post-stop suspend sound, and sound volume override settings.
- Do not let Emergency Hibernation webhook timeout or failure block the immediate hibernation request.
- Emergency Hibernation takes priority over any pending ask-before-sleep reply wait and must cancel that wait before hibernate/Sleep fallback handling continues.