Repository navigation
acpmux: an idle chat hibernates after 2 min, the one on screen too; typing wakes it - #26127
Conversation
|
Owned by an agent session · session |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
web and size checks: green |
|
Bead |
|
Found 3 test failures on Blacksmith runners: Failures
|
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
|
|
Orphaned: no activity for 13 h, and no lane holds a lease on it. The orphan sweep could not merge |
…every hibernation and wake is logged Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3891b16636fa80ffc495
8c76472 to
a93fbf4
Compare
… mark Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3891b16636fa80ffc495
|
The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/40697064264 at 3d58f37 failed: cmux-next checks (god files, concurrency, crash safety, l10n). |
|
Shipped in:
|
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Size: 1081 counted lines, of which 503 are tests and 58 docs; the code is 520. Hibernation, its wake and its gates only work together, and each test stays with the code it covers, so this lands as one PR.
Leo (2026-10-21), the ask this builds, word for word:
Also Leo: "WHY THE FUCK DO WE NOT HAVE MORE AGGRESSIVE FUKCING HIBERNATION" (10-21), and "We need to be able to, like, ultra-aggressively hibernate. And, like, when things wake up, you know..." (10-23).
Before
acpmux already had an idle exit (
hub/idle.rs, since 2026-10-04), but it was 5 minutes and it skipped every chat a client was attached to. The app attaches the chats it shows and keeps the pane's chats attached, so the chats people actually leave open never hibernated: their Claude Code or Codex process and every MCP server it started stayed up until the daemon stopped.After
hub::IDLE_CHILD, still overridable withACPMUX_IDLE_CHILD_SECS; there is no settings page for it, so no new setting). Idle means: no turn running or waiting (Claude Code's own task-notification turns included), nothing queued, no pending permission or question, no start in progress, no background command or background subagent running, and nothing logged for 2 minutes.detach_childends the harness's process group, and with it the stdio MCP servers it started. The session's record, transcript and agent session id stay. The pane is not touched (no reload, no new transcript row: the test checks that onlystatusandstoppedrecords are written, and the pane draws neither).claude --resume, ACPsession/load), the existing path for a reaped or restarted session. The prompt is accepted and drawn at once, before the agent starts, as it already was. Typing into a hibernated chat (_acpmux/typingon, which the pane already sends on the first keystroke) or opening it on screen (_acpmux/attachshown,_acpmux/viewvisible; an authored collaborator view does not) starts the wake right away, so the agent is usually up by the time Enter is pressed. A hibernation survives a daemon restart (SessionMeta::hibernated_ms). A hibernation and its wake add no transcript row in the pane or the TUI (stopped/exitedmarkedhibernated,resumedmarkedwake, and the replayed config record draw nothing). A wake respects the folder-trust gate like the warm path.turn_resultwith the error, which the chat shows with Retry. The test covers that, and that the retry then wakes it.hibernatedwithcause=idle, how long it was idle and how long the exit took. Every wake logswoke(orwake failed) with its cause (typing,prompt, or another request that needs the agent), how long the start took, and how long it slept.Never hibernated: a running turn (and so a running tool call or terminal command), a queued prompt, a pending permission or question, a background shell or subagent, a start holding the spawn lock, and any session while the daemon shuts down.
Scope (what this PR owns, and what it doesn't)
This PR owns the 2-minute idle threshold and what counts as idle, waking on a prompt or a click (typing), the wake logging, and the bench. It adds no kill or retirement code. The exit goes through the existing
detach_child. The PID-only retirement foundation lands separately on its own item and this PR rebases onto it before landing: a bounded TERM, then KILL, of only the harness and MCP sidecar handles acpmux holds; the session lease plus generation fence; and the holder idle-generation query. Until then, the idle exit holds the session'sturn_lockandspawn_lockwhile it ends the harness, so a prompt or a start can't race the exit. The lease replaces that hold on rebase.Out of scope by design: detached work that escaped the harness (a
setsid'd server) survives a hibernation, because nothing signals it.Not in this PR
"Sessions waiting on a person stay cheap too: they keep only the question." A session waiting on a permission or question still keeps its agent here. Parking it means ending the waiting turn, keeping the question card live, and turning the answer into the woken agent's next input. That needs pane work and its own review, so it goes in a follow-up PR. This PR keeps those sessions awake, which is today's behaviour.
Timers
One task per hub, the existing idle reaper (
idle_reaper_loop). It waits on the injected clock for the earliest idle deadline and on a notify. It sleeps with no deadline while no session has a live agent. It ends when the hub is dropped (its weak reference), and it stops for good when shutdown starts (stop_idle_reaper). No new timer. The typing wake is one task per wake that ends when the start ends. Typing also restarts the idle period, so a chat being typed into never hibernates.Tests
tests/hub_integration/session_hibernate.rs, on the hub's injected clock, with the fake harness under a shell that starts a stand-in MCP server in its process group and records both pids:ps), not before 2 minutes, and no transcript row is written;resumed, and the prompt sent to the agent exactly once;lifecycle_fixes.rs: the old "an attached session's harness stays" assertion is now the opposite (an attached session hibernates too).Proof
hub_integration522 passed (1 ignored),acpmux --libidle, transcript and store 72 passed,durable_agentsidle 9 passed, clippy-D warningsclean.bench=hibernate(scripts/measure/acpmux-overhead.sh) runs after this lands. RSS before and after (a no-tools session) and the wake time are measured then and added here.Risk
Medium. More harness restarts: a chat left open and idle for 2 minutes pays a resume on its next prompt. The typing wake hides most of that. Every resume runs the existing
session/load/--resumepath, the same one a daemon restart or the old 5-minute idle exit takes. A harness whose resume is slow (Claude Code with many MCP servers) makes the first prompt after a pause slower. The bench numbers above measure that.Review (codex gpt-6-astra high, fast): SHIP at 05f3169 (earlier rounds at xhigh: SHIP at 8c76472 and at a93fbf4 after the rebase onto #26174; this round checks the lease drop after the stored mark and the swap to the foundation API).
Agent session: agent session
🤖 Generated with Claude Code