cmux NEXT: a chat's composer chips and shell command come back after a relaunch
Leo, 2026-10-20: "After nightlies, have the things running, but have the views go back to where they were."
After a relaunch, an agent chat's composer now comes back with its attached images and files and its unrun shell-mode command. Before this, that state (AgentPaneWakeState) lived only in memory, in AgentTabStore.wakeStates, which hibernation fills when it releases a hidden chat page. So every relaunch dropped it: Restart to Update, SIGTERM's Quit and keep sessions (tonight's 17:51 relaunch), and Cmd-Q.
There is one save path for every kind of relaunch: the quit's prepareWindows step, which all three run. It asks each live chat page for pane.wakeState. The pages are asked all at once, each within 1 s, and the ask only reads. A page still loading keeps the state it was built with, and released pages' kept states are added. The result is written to agent-pane-wake-states.json beside the launch snapshot. Tabs that are no longer in any tree are dropped. Each tab can hold at most 4 MiB and the file at most 8 MiB, keeping the smallest tabs first. At launch the tab store reads the file once and deletes it, and each tab's first page gets its state with its ready handshake, the same way a woken hibernated page does. The file holds attachment contents as plain JSON in the tag's state directory, next to the existing launch images.
What else is restored today (audit)
| State | Restored after a relaunch? | Where it is kept |
|---|---|---|
| Terminals, panes, tabs, layout | yes | cmux-tui daemon |
| Window frame, display, order, full screen, closed windows | yes | daemon personal projection (WindowRecordSaver), flushed in prepareForTermination |
| Selected tab per pane, focused pane per workspace, active screen, top page | yes | WindowRecord |
| Sidebar width, sidebar hidden, sidebar group folds | yes | WindowRecord / sidebar doc |
| Agent chat draft text | yes | acpmux (#21509); a new chat's text in AgentUnsentDrafts |
| Agent chat scroll place | yes | agent-scroll-anchors.json (chat.scrollAnchor) |
| Agent pane first frame | yes | agent-pane-images/ launch images |
| Browser tab page and scroll | yes | browser tab records, flushed at quit |
| Composer attachments and shell-mode command | no, fixed here | was memory only |
| Terminal viewport scrolled up into scrollback | no | comes back at the bottom; needs daemon/Ghostty support, not in this PR |
| Agent pane inspector, diff view, subagent view | no | page-local React state; follow-up |
Changelog
Fixed: Agent chats keep their attached images and files and an unrun shell command across an app relaunch or update.
Tests
cmux-ci run --class light --script scripts/ci/package-test-lane.sh --arg suite --arg Packages/macOS/CmuxNext --arg <suites> --ref <sha>
- Red on f21f21b4 (the test, with the seam stubbed): step f1cd4f0c9bbc7f7481450b02 failed.
aChatsComposerComesBackAfterARelaunch: "the live chat came back without its composer".anEmptyComposerLeavesNoFile: "a held shell command was not saved". - Green on af8744b8: step 9ea6ee2944d89269b4a646be passed.
AgentPaneRelaunchWakeTestspassed (9 s),AgentPageHibernationTestspassed (17 s),UnsentDraftTestspassed (1 s).
Observed / Not run
Observed: the fleet red and green runs above, and python3 scripts/cmux-next/prepush-gate.py with no new violations on every push. An independent review subagent passed it with nits; the nits are fixed in af8744b8 (prune closed tabs, keep a loading page's state, lower the size caps).
Not run: a real relaunch of a tagged build (Restart to Update or SIGTERM) with chips in a composer, so the page-side wake apply after a relaunch is covered only by the existing hibernation path, not seen live. No app build was made on the Mac.
🤖 Generated with Claude Code
Agent session: cosmic-gray