Files
boocode/openspec/changes/pty-exit-notifications/specs/pty-exit-notification/spec.md
indifferentketchup b18de2a331 chore: snapshot working tree - pty_exited notifications + in-flight inference WIP
feat(booterm): structured pty_exited WS notifications. Plan-validated, impl-validated, code-reviewed green (contracts build clean, contracts test 29/29, booterm + web typecheck clean).

wip: in-progress inference/provider refactor (agents.ts, provider.ts, new llama-providers.ts, removed llama-args-validator), plus arena, dispatcher, compaction, schema changes.

openspec: pty-exit-notifications complete; x-agent-flags planned (not yet implemented).
2026-06-14 12:48:47 +00:00

3.4 KiB

ADDED Requirements

Requirement: Structured pty_exited frame on WS protocol

The system MUST send a structured exit notification when a PTY process exits.

  • WHEN a process running in a booterm terminal pane exits (via handle.onExit)
  • THEN booterm MUST send a structured pty_exited JSON text frame on the WS connection containing: type, exit_code, last_lines (array of recent output lines from the ring buffer), session_id, session_title, session_description, parent_agent, timed_out (boolean)

Scenario: Normal process exit with metadata

  • WHEN a user's SSH shell process exits with code 0 after producing output
  • AND the terminal pane was registered with title "build", description "run tests", parentAgent "claude"
  • THEN the pty_exited frame MUST contain exit_code: 0, at least one last_lines entry, session_title: "build", session_description: "run tests", parent_agent: "claude", and timed_out: false

Scenario: Process exit with no output

  • WHEN a process exits immediately without producing output
  • THEN the pty_exited frame MUST contain an empty last_lines array and valid session metadata

Scenario: Timeout-triggered exit

  • WHEN a process is killed by the idle timeout sweep (requires sweepExpired to be wired to an interval, which is a separate change)
  • THEN the pty_exited frame MUST contain timed_out: true and the exit code from the tmux kill

Requirement: pty_exited frame type in WsFrame contract

The system MUST register pty_exited as a valid frame type in the cross-app wire contract.

  • WHEN the pty_exited frame schema is added to WsFrameSchema in packages/contracts/src/ws-frames.ts
  • THEN it MUST be included in KNOWN_FRAME_TYPES and validate against the discriminated union

Scenario: Frame validates against schema

  • WHEN a pty_exited frame with all required fields is parsed
  • THEN the Zod validation MUST pass and the frame MUST NOT be dropped

Scenario: Frame missing required fields

  • WHEN a pty_exited frame is missing the exit_code field
  • THEN the Zod validation MUST fail and the frame MUST be dropped with a log warning

Requirement: Client parse of pty_exited frame

The web frontend MUST recognize and parse pty_exited frames from the booterm WS.

  • WHEN the web frontend receives a pty_exited frame over the terminal WS
  • THEN parseServerFrame MUST recognize it and return a structured object with session_id, pane_id, exit_code, last_lines, and session metadata

Scenario: Client receives pty_exited

  • WHEN the browser receives a pty_exited frame
  • THEN the terminal MUST display a styled exit notification with the exit code and last output line(s)

Scenario: Client receives pty_exited with timeout

  • WHEN the browser receives a pty_exited frame with timed_out: true
  • THEN the terminal MUST display a timeout-specific notification message

Requirement: Backward compatibility with bare exit frame

The client MUST NOT break when receiving the legacy bare exit frame.

  • WHEN a booterm instance sends the old {type: 'exit', code: N} frame (pre-upgrade)
  • THEN the client MUST gracefully handle it as before (display exit message, no crash)

Scenario: Legacy exit frame received

  • WHEN the client receives {type: 'exit', code: 1}
  • THEN the terminal MUST display the exit code message without throwing