cmux NEXT: agent page crash reason, pid and group Reload (28710)
Fixes https://github.com/manaflow-ai/cmux/issues/28710
Problem. Agent chats share WebContent processes (8 pages per process). When one of those processes ended, every chat in it showed "The agent pane crashed." cmux logged only agent pane web content ended, with no reason, pid, page or count of chats that ended with it. Each chat also needed its own Reload.
Change
- WebKit's
_webView:webContentProcessDidTerminateWithReason:SPI now reports why the process ended (memory limit, CPU limit, requested by client, or crash). If this WebKit doesn't provide it, the reason isunknown. - New
AgentPageTerminationsgroups the pages that ended together. A page joins a group when it has the same WebContent pid, or when the pid is unknown and the page reported in the same main-loop turn. The pid comes from_webProcessIdentifier, with the pid recorded at the last page load as a fallback. - For each page that ends, an error-level log line gives the reason, pid, page/session, engine and how many pages ended with it. A summary line follows for the group, plus one crash marker JSON in
~/Library/Logs/cmux-next/<bundle>-webkit-agent-page-<time>-<n>.json. Like the Chromium renderer marker, it holds no typed text, URL or title. - Reload in any crashed chat reloads every chat from the same termination that still shows the notice. Chats that didn't crash, chats from another termination and chats that were closed are left alone.
- Nothing reloads by itself. The never-reload rule from https://github.com/manaflow-ai/cmux/pull/25791 and https://github.com/manaflow-ai/cmux/pull/25920 still holds.
Tests (AgentPageTerminationsTests, crash events injected, no real process kill)
- Two chats end in one process: they form one group with its reason and pid, and one marker is written. One Reload reloads both. A chat that didn't crash and a chat from another termination are not reloaded.
- WebKit's reason SPI reaches the record.
- A closed chat leaves its group, so a sibling's Reload doesn't reload it.
- On the shared page host, the page keeps the reason and pid, and Reload reloads the sibling's page too.
Crash ratchet. CmuxNextPages dynamic_dispatch goes from 1 to 3 for the KVC read of _webProcessIdentifier, which runs only after responds(to:) succeeds. The correctness-review subagent signed this off, and the commit carries the Crash-allow-OK trailer.
Review. A correctness-review subagent returned FIX with these findings:
- Markers written in the same millisecond collided on one filename. Fixed: the filename now includes the event serial.
- A closed chat could still be reloaded by a sibling's Reload. Fixed:
close()now callsforget(_:). - The pid was 0 after the process died. Fixed: the pid recorded at the last load is used as a fallback.
- Accepted as a known heuristic: a page whose pid is 0 joins a group with the same reason from the same turn.
Validation. Headless Swift suites run on cmux-ci at 7013df0c629f0588132326ff41270d83b30564d8 (key 28710-crash-notice-7013df0-suites): AgentPageTerminationsTests, AgentPageHibernationTests, PageWebViewCrashReloadTests, WebProcessGroupsTests, BrowserNeverReloadsByItselfTests. All 5 suites passed at ac2c1d8f300d56dc24501c0407c17c665efca66e (cmux-ci step 3843c6f996b6b20a85b1345b, cmux7s-Mac-mini). The earlier run at 7013df0c629 is superseded: it failed to compile, fixed in ac2c1d8.
No capture: nothing visible changes. The crash notice keeps its text and layout; only logging, the crash marker file and which chats Reload reloads change. That behavior is covered by AgentPageTerminationsTests.
š¤ Generated with Claude Code