Repository navigation
Hibernation: don't let an unknown lifecycle clobber a proven idle - #6693
lawrencecchen wants to merge 2 commits into
Conversation
Non-codex agents rarely hibernated when idle off-screen: only codex did. A restarting/resuming agent (and plugin agents with no live lifecycle) emit an `.unknown` lifecycle on SessionStart, which overwrote the `.idle` the agent had reported at its previous turn end. The hibernation reader intentionally ranks `unknown` above `idle` (an unclassified agent should block hibernation), so once the stored state decayed to `.unknown` the panel never became eligible again. Fix the write path instead of the read priority: `setAgentLifecycle` now applies `AgentHibernationLifecycleState.preservingDefinitive`, so an incoming `.unknown` is kept only when there is no proven definitive state to preserve. A definitive incoming state (idle/running/needsInput) always wins, so eligibility stays positive-evidence-only and we never invent idleness. Also stop treating an `.unknown` write as activity: `recordAgentLifecycleChange` resets the idle countdown, so a periodic `.unknown` heartbeat would otherwise keep a genuinely idle agent from ever accumulating enough idle time to hibernate. Unit tests cover the pure resolution table plus the two Workspace behaviors (unknown preserves stored idle; a real running still overrides it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 3 minutes and 13 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR fixes agent hibernation for non-codex agents by preventing an
Confidence Score: 5/5Safe to merge — changes are tightly scoped to the lifecycle write path and PID registration boundary, both backed by targeted regression tests. The preservingDefinitive function is a pure, deterministic merge with no side effects and is fully covered by a truth-table test. The setAgentLifecycle change is contained to two decisions (which value to write, whether to record activity) with clear invariants. The PID-boundary clear in recordAgentPID follows the same agentStatusKey key-resolution pattern used throughout the file. No actor isolation issues, no blocking primitives, and no ambient global state were introduced. No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "Hibernation: clear a key's stale lifecyc..." | Re-trigger Greptile |
…replaced Autoreview caught that applying preservingDefinitive to every `.unknown` write also blocks an explicit `set_agent_lifecycle <key> unknown` from clearing a prior `.idle` across a process restart. A new agent runtime (new PID) for the same key would then inherit the dead process's `.idle` and could be hibernated on stale evidence. Scope preservation to a single runtime: when `recordAgentPID` sees a different PID for a key, clear that key's stored lifecycle so the new process starts from no evidence (and the idle countdown restarts). Within one runtime an `.unknown` heartbeat still preserves a proven definitive state; across a runtime boundary it no longer does. Adds a test: same-PID re-register preserves idle; a new PID clears it so a following `.unknown` stays unknown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e943c78. Configure here.
| // starts from no evidence and the idle countdown restarts. | ||
| if let panelId, let previousPID = agentPIDs[key], previousPID != pid { | ||
| _ = clearAgentLifecycle(key: agentStatusKey(forAgentPIDKey: key), panelId: panelId) | ||
| } |
There was a problem hiding this comment.
PID change skips clear without panel
Medium Severity
When recordAgentPID sees a new PID for an existing key, lifecycle is cleared only if panelId is non-nil. Hooks and sidebar paths often call recordAgentPID with a nil panel while still updating the global PID map. After this change, a following .unknown lifecycle report no longer overwrites a stored .idle, so stale idle from the previous process can remain and make hibernation treat a new runtime as eligible.
Reviewed by Cursor Bugbot for commit e943c78. Configure here.
|
Closing after deeper review: this fix targets a scenario that does not occur on current Findings from first-principles verification against current
The actual non-codex hibernation gaps on current |


Problem
Agent Hibernation only hibernated agents whose lifecycle resolved to
idle, and in practice mostly only codex got there. A restarting/resuming agent (and plugin agents that emit no live lifecycle) reports.unknownon SessionStart, which overwrote the.idlethe agent had recorded at its previous turn end. The hibernation reader intentionally ranksunknownaboveidle(an unclassified agent should block hibernation), so once the stored state decayed to.unknownthe panel never became eligible again.Fix
Fix the write path, not the read priority.
Workspace.setAgentLifecyclenow appliesAgentHibernationLifecycleState.preservingDefinitive: an incoming.unknownis kept only when there is no proven definitive state to preserve. A definitive incoming state (idle/running/needsInput) always wins, so eligibility stays positive-evidence-only and we never invent idleness.It also stops treating an
.unknownwrite as activity.recordAgentLifecycleChangeresets the idle countdown, so a periodic.unknownheartbeat would otherwise keep a genuinely idle agent from ever accumulating enough idle time to hibernate.This is the smallest, single-consumer slice of the broader hibernation work in #5500, split out so it can land independently.
Tests
cmuxTests/AgentHibernationTests.swift: the purepreservingDefinitiveresolution table, plus two Workspace behaviors (an.unknownwrite preserves a stored.idle; a real.runningstill overrides it).🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Note
Medium Risk
Changes hibernation eligibility and idle-timer semantics; wrong merging could hibernate live agents or block hibernation, but scope is limited to lifecycle write paths with strong test coverage.
Overview
Fixes agent hibernation so
.unknownlifecycle writes no longer wipe a stored.idle/.running/.needsInput, which had left many non-codex panels permanently ineligible after SessionStart or silent plugin agents.AgentHibernationLifecycleState.preservingDefinitivemerges incoming vs existing state: definitive incoming values still win;.unknownonly applies when there is no prior definitive state.Workspace.setAgentLifecycleuses that merge and skipsrecordAgentLifecycleChangefor.unknown, so idle heartbeats do not reset the hibernation idle timer.When
recordAgentPIDsees a new PID for the same agent key, it clears stored lifecycle so a replacement process is not hibernated on the old process’s idle evidence (same-PID re-register keeps lifecycle).Tests cover the merge table, workspace behavior, and PID-boundary clearing.
Reviewed by Cursor Bugbot for commit e943c78. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Prevent
.unknownlifecycle reports from wiping a proven.idle, and clear stale state when an agent runtime (PID) changes, so non-codex agents can hibernate again. Also stop.unknownheartbeats from resetting idle timers.AgentHibernationLifecycleState.preservingDefinitiveand used it inWorkspace.setAgentLifecycleto keep a definitive stored state when the incoming value is.unknown; definitive incoming values still override..unknownas activity inWorkspace.setAgentLifecycle; only definitive states triggerrecordAgentLifecycleChange, allowing genuine idle time to accrue.recordAgentPIDwhen its PID changes, so a new runtime doesn’t inherit a prior runtime’s.idleand the idle countdown restarts.Written for commit e943c78. Summary will update on new commits.