## Why When a process running in a booterm terminal pane exits, the browser currently receives a bare `{type: 'exit', code: N}` frame and the socket closes (`apps/booterm/src/ws/attach.ts:170-183`). There is no structured metadata: no last output lines, no session title, no parent agent attribution. The inference loop in apps/server and apps/coder cannot react when a long-running task completes because the notification carries no context beyond the exit code. The reference implementation (`/opt/forks/opencode-extras/opencode-pty`) solves this with `` structured notifications carrying exit code, last output lines, session metadata, and timeout status. Booterm already tracks all of this data (registry `SessionMeta` with `sessionId`, `paneId`, `title`, `description`, `parentAgent`; ring buffer with output lines via `appendOutput`). The data is present but never surfaced on exit. ## What Changes - Enhance the booterm WS exit notification from a bare `{type: 'exit', code}` to a structured `pty_exited` frame carrying: exit code, last N output lines from the ring buffer, session metadata (title, description, parentAgent), and timeout status. - Add `pty_exited` as a new frame type in the cross-app WsFrame contract (`packages/contracts`). - Update the web frontend to parse and handle the new frame type. ## Scope - **In scope**: structured exit notification over booterm WS; new WsFrame type in contracts; web frontend handling. - **Out of scope**: log-search extras (already implemented in booterm registry ring buffer + search route), per-session timeouts (already implemented in registry + sweepExpired), pattern-based PTY log search (already in `searchRingBuffer`). These exist; this change only adds the exit notification. Broker publish for inference-loop consumption is deferred (see Deferred section). ## Non-goals - Changing the booterm WS binary/text frame protocol for ongoing data. - Adding persistence for exit events (no DB table; frames are ephemeral like all broker frames). - Modifying the coder's PTY dispatch flow (which uses `child_process.spawn`, not booterm PTYs).