Stop burning CPU on decoration: idle mascots, hidden polls, lazy sidecar - #425
Merged
Conversation
An iPhone simulator left on the roster overnight sat at ~41% CPU with SimRenderServer at ~38% — every chat row mounts a mascot face, and animation was opt-out. Both platforms now treat motion as signal: - iOS: animated defaults to false in MausAvatar/BotAvatarView/ ChatAvatarView; faces opt in only where motion means something (a bot actively listening/thinking/searching/working, the open profile sheet, the needs-you island). An opted-in face still stops when the app is not active (scenePhase), and the island's comet orbit — the costliest draw in the app — goes still after 30s so an unattended approval cannot keep a 30fps orbit running until morning. - Desktop: the mascot rAF loop re-armed BEFORE its paused check, so even paused faces woke at display rate; paused now parks the loop on a 4Hz wake-poll. Sidebar rows animate only for busy/unread/motion-beat bots, and the decorative strips are still. - Computer drawer: every preview poll (cloud 4s, VM 3s, host ScreenCaptureKit 3s, adb devices 2s, phone screencap 850ms) now gates on document visibility and drops to a 30s heartbeat for idle bots. - Companion sidecar: the interface-table poll relaxes 5s -> 30s; it is the process's only recurring wakeup and networks change on the minutes scale. Deliberately NOT here: pausing idle Local VM containers (the other big idle burner). docker pause reports Paused containers as Running, so the status parser and every consumer need to learn the state and unpause lazily at the turn/screenshot/panel choke points — a follow-up, not a rider on a decoration patch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (13)
📝 WalkthroughWalkthroughThe change reduces background polling and animation. It adds page visibility handling for web capture, makes avatar animation opt-in, enables activity-based animation in iOS and web views, and increases companion address-watch polling to 30 seconds. ChangesiOS avatar activity
Web visibility-aware polling
Companion advertisement polling
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant usePageVisible
participant ComputerPanel
participant CaptureSource
Browser->>usePageVisible: emit visibilitychange
usePageVisible->>ComputerPanel: provide visible state
ComputerPanel->>CaptureSource: poll at activity-based cadence
ComputerPanel->>CaptureSource: pause polling when hidden
Suggested reviewers: ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
milind-soni
added a commit
that referenced
this pull request
Aug 24, 2026
#425 made sidebar mascots mount paused — and exposed that the parked loop never draws: the SVG layers hold no expression until the first draw() positions them, so an idle bot's avatar rendered blank. The paused branch now paints the still face once, re-painting only when what it shows changes (state, pinned expression, gradient), then parks on the same 4Hz wake-poll. Animation stays opt-in; the resting pose is simply visible again. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
milind-soni
added a commit
that referenced
this pull request
Aug 25, 2026
…#442) * feat(ui): composer attach button and per-bot permission mode selector Adds a paperclip button that opens a file picker feeding the shared attachment pipeline, and an Approve-for-me / Ask-for-approval pill that toggles autoApprove per bot without opening settings. The composer is restructured into two rows — text on top, controls below — matching common chat-app layouts. * feat(ui): remove the Always allow button from approval cards The per-bot permission mode selector in the composer (Ask for approval / Approve for me) is the single mechanism for reducing approval prompts; the per-program Always-allow grant duplicated it with a worse model. Allow-once and Deny remain. * Paint the resting face when a mascot mounts paused (#444) #425 made sidebar mascots mount paused — and exposed that the parked loop never draws: the SVG layers hold no expression until the first draw() positions them, so an idle bot's avatar rendered blank. The paused branch now paints the still face once, re-painting only when what it shows changes (state, pinned expression, gradient), then parks on the same 4Hz wake-poll. Animation stays opt-in; the resting pose is simply visible again. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * fix(composio): preload connected account state (#445) * chore(release): bump version to 0.1.33 (#446) * Add portable BotMRR Markdown playbooks (#426) * Add portable BotMRR package installs * Keep package link tests out of Vitest discovery * Add universal BotMRR Markdown imports * Document portable team playbooks * fix(composio): accept empty authorization bodies (#451) * ci: stop retaining disposable Linux packages (#452) * fix(linux): harden Ubuntu upgrades and Xorg local control (#346) * fix(linux): fail closed on unsafe local control startup * fix(desktop): keep optional connected apps off startup path * fix(linux): repair inherited DEB upgrade permissions * docs(linux): document the local-control safety hold * fix(ci): configure dependencies in DEB upgrade smoke * fix(linux): configure DEB Chromium sandbox * fix(linux): restore safe Xorg local control * fix(desktop): close review security and refresh races * fix(linux): isolate local control safety opt-in * docs(linux): explain private CUA cursor behavior * fix(linux): clean CUA runtime on termination signals * fix(linux): reap stale AppImage CUA stages * docs(linux): clarify release CUA coverage * test(linux): preserve packaged smoke diagnostics * ci(linux): normalize runner package parent * fix(linux): close final Ubuntu review gaps * fix(ci): fail closed before Ubuntu package install * fix(composio): enforce broker URL parity * Let Antigravity models control computers (mount the computer MCP) (#447) * Let Antigravity models control computers (mount the computer MCP) agy has no per-turn MCP flag and provably no project-level MCP config (1.1.19: embedded docs list only the global ~/.gemini/config/mcp_config.json and per-plugin files; agy mcp list ignores .gemini/{settings,mcp_config}.json in the cwd). So each turn upserts one key — openmausbot-computer — into the global file right before the spawn, preserving every other byte of the user's config and tolerating malformed JSON, and removes that key on the next computer-less turn so tools and box/control tokens cannot leak into later turns or the user's own agy sessions. Cloud boxes mount OpenMausBot's REST-to-MCP computer proxy (resolved via SPAWNED_PROXIES — never relative to the module, the 0.1.24 lesson); Local VM and VPS connections pass through as the stdio Cua command they already are. computerMcp is advertised only by full-auto instances: print mode has no interactive approval channel, and outside --dangerously-skip-permissions agy auto-denies tools that would prompt, so a non-fullAuto mount could never fire. localComputerMcp stays unset — the host desktop requires per-action human approval, which print mode cannot deliver in any mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: isolate Antigravity computer mounts * fix: reap settled Antigravity children * fix: keep MCP lease until child exit * fix: preserve Antigravity MCP ownership * fix: clear failed Antigravity turns --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Milind Soni <46266943+milind-soni@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Kesley David <39314443+KesleyDavid@users.noreply.github.com> Co-authored-by: milind-soni <milindsoni201@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
An iPhone 17 Pro simulator left on the roster overnight sat at ~41% CPU (OpenMausCompanion) with SimRenderServer at ~38% for 8 hours. Two audits traced it: every mounted mascot face was an always-on animation loop — 30fps
TimelineView+Canvasper chat row on iOS, a 60fps rAF loop per avatar on desktop whosepausedcheck ran after re-arming — and the computer drawer's preview polls ran at full cadence regardless of visibility or bot activity. On a real phone this is battery, not just heat.What changed
Motion is now signal, not decoration — animation is opt-in on both platforms.
animateddefaults tofalseinMausAvatar/BotAvatarView/ChatAvatarView. Opt-in sites: roster rows and the chat header only while the bot's state shows activity (listening/thinking/searching/working — newMausState.showsActivity), the open profile sheet, and the needs-you island. Opted-in faces also stop whenscenePhase != .active, and the island's comet orbit (the costliest draw path) goes still 30s after appearing, so an unattended approval can't keep a 30fps orbit running until morning.adb devices(2s), and phone screencap (850ms) polls all gate ondocument.visibilityStateand drop to a 30s heartbeat when the bot is idle.Deliberately deferred
Pausing idle Local VM containers (the other big idle burner: a full Linux desktop per bot runs for 8h after last use).
docker pausereports paused containers asRunning, so the status parser and the turn/screenshot/panel choke points all need to learn the state and unpause lazily — that deserves its own PR against the fresh #332 lease fences, not a rider here. Design notes in the commit message.Verification
🤖 Generated with Claude Code
Summary by CodeRabbit