mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
perf(desktop): stretch backstop polls while on battery
powerMonitor's AC/battery state is mirrored to the renderers (store/power.ts) and visiblePoll quadruples its cadence on battery. Only the safety-net refreshes slow down — event-driven refreshes and live streaming are untouched.
This commit is contained in:
parent
be7c4b8fe7
commit
044800e358
5 changed files with 55 additions and 2 deletions
|
|
@ -275,6 +275,14 @@ contextBridge.exposeInMainWorld('hermesDesktop', {
|
|||
|
||||
return () => ipcRenderer.removeListener('hermes:power-resume', listener)
|
||||
},
|
||||
// AC ↔ battery transitions; renderers slow their backstop polls on battery.
|
||||
getOnBattery: () => ipcRenderer.invoke('hermes:power-battery:get'),
|
||||
onBatteryChanged: callback => {
|
||||
const listener = (_event, onBattery) => callback(Boolean(onBattery))
|
||||
ipcRenderer.on('hermes:power-battery', listener)
|
||||
|
||||
return () => ipcRenderer.removeListener('hermes:power-battery', listener)
|
||||
},
|
||||
onBootProgress: callback => {
|
||||
const listener = (_event, payload) => callback(payload)
|
||||
ipcRenderer.on('hermes:boot-progress', listener)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useEffect } from 'react'
|
|||
|
||||
import { createClientSessionState } from '@/lib/chat-runtime'
|
||||
import { $changeEventsAvailable, $cronChangeTick, $sessionsChangeTick } from '@/store/live-sync'
|
||||
import { $onBattery, batteryPollInterval } from '@/store/power'
|
||||
import { refreshActiveProfile } from '@/store/profile'
|
||||
import { $activeSessionId, $currentCwd, setCurrentCwd } from '@/store/session'
|
||||
import {
|
||||
|
|
@ -180,7 +181,10 @@ interface BackgroundSyncParams {
|
|||
}
|
||||
|
||||
/** Poll a callback while the tab is visible, on `intervalMs`; re-checks on tab
|
||||
* re-focus. Returns nothing — meant to live inside an effect. */
|
||||
* re-focus. On battery the cadence stretches (see store/power) — these are
|
||||
* safety-net refreshes, not the live path, so they're the right thing to slow
|
||||
* when the machine is spending its charge. Returns nothing — meant to live
|
||||
* inside an effect. */
|
||||
function visiblePoll(intervalMs: number, tick: () => void): () => void {
|
||||
const run = () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
|
|
@ -188,10 +192,17 @@ function visiblePoll(intervalMs: number, tick: () => void): () => void {
|
|||
}
|
||||
}
|
||||
|
||||
const intervalId = window.setInterval(run, intervalMs)
|
||||
let intervalId = window.setInterval(run, batteryPollInterval(intervalMs, $onBattery.get()))
|
||||
|
||||
const unsubscribeBattery = $onBattery.listen(onBattery => {
|
||||
window.clearInterval(intervalId)
|
||||
intervalId = window.setInterval(run, batteryPollInterval(intervalMs, onBattery))
|
||||
})
|
||||
|
||||
document.addEventListener('visibilitychange', run)
|
||||
|
||||
return () => {
|
||||
unsubscribeBattery()
|
||||
window.clearInterval(intervalId)
|
||||
document.removeEventListener('visibilitychange', run)
|
||||
}
|
||||
|
|
|
|||
2
apps/desktop/src/global.d.ts
vendored
2
apps/desktop/src/global.d.ts
vendored
|
|
@ -256,6 +256,8 @@ declare global {
|
|||
// reload. Wipe session lists (skeletons) and re-dial.
|
||||
onConnectionApplied?: (callback: () => void) => () => void
|
||||
onPowerResume?: (callback: () => void) => () => void
|
||||
getOnBattery?: () => Promise<boolean>
|
||||
onBatteryChanged?: (callback: (onBattery: boolean) => void) => () => void
|
||||
onBootProgress: (callback: (payload: DesktopBootProgress) => void) => () => void
|
||||
getBootstrapState: () => Promise<DesktopBootstrapState>
|
||||
continueBootstrapLocal: () => Promise<{ ok: boolean }>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import './styles.css'
|
||||
// Side-effect: reports in-flight turns to the main process for the quit guard.
|
||||
import './store/active-work'
|
||||
// Side-effect: mirrors the machine's AC/battery state for poll demotion.
|
||||
import './store/power'
|
||||
// Side-effect: applies the persisted window translucency on load.
|
||||
import './store/translucency'
|
||||
// Dev-only render/state churn counters. MUST precede the `react-dom` import
|
||||
|
|
|
|||
30
apps/desktop/src/store/power.ts
Normal file
30
apps/desktop/src/store/power.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* AC ↔ battery mirror of the main process's powerMonitor.
|
||||
*
|
||||
* Backstop polls are already event-demoted and visibility-gated; battery is
|
||||
* the third gate — on battery their cadence stretches (×4) so a laptop
|
||||
* running Hermes in the background isn't spending its charge on safety-net
|
||||
* refreshes. Live streaming and event-driven refreshes are untouched: this
|
||||
* only slows timers whose job is catching what a degraded socket missed.
|
||||
*
|
||||
* Imported for its side effect from `main.tsx` (same pattern as
|
||||
* store/active-work).
|
||||
*/
|
||||
|
||||
import { atom } from 'nanostores'
|
||||
|
||||
export const $onBattery = atom<boolean>(false)
|
||||
|
||||
/** Multiply a backstop poll interval by this on battery. */
|
||||
export const BATTERY_POLL_MULTIPLIER = 4
|
||||
|
||||
export function batteryPollInterval(intervalMs: number, onBattery: boolean): number {
|
||||
return onBattery ? intervalMs * BATTERY_POLL_MULTIPLIER : intervalMs
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
const desktop = window.hermesDesktop
|
||||
|
||||
void desktop?.getOnBattery?.().then(onBattery => $onBattery.set(onBattery))
|
||||
desktop?.onBatteryChanged?.(onBattery => $onBattery.set(onBattery))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue