Files
Developer caadd59441 chore: archive 15 completed OpenSpec changes
Move the following audited-and-implemented changes into
openspec/changes/archive/2026-06-12-completed-changes-archive/:

- backend-frontend-refactoring
- config-profile-git-mounts
- config-profile-includes-ui
- config-profile-multi-repo-mounts
- container-monitoring-notifications
- git-mount-url-validation
- home-path-expansion
- mobile-terminal-ux
- mount-specificity-ordering
- notification-center
- persistent-terminal-sessions
- session-list-overhaul
- ssh-key-mounting
- terminal-fullscreen-unified-header
- tool-session-progress-and-updates

Also regenerated .pi-map*.md files for openspec/changes so the
remaining active changes (multi-session-terminal-ux, reorganize-long-files,
working-copies, workspace-first-ui) reflect the new layout.
2026-06-12 14:26:55 +00:00

3.8 KiB

Context

Currently, terminal sessions are ephemeral. Each WebSocket connection to /ws/tool-instances/{instance_id}/terminal spawns a new docker exec process via TerminalManager.create_session(). When the WebSocket disconnects, the session is cleaned up and the docker exec process is killed. This means users lose their shell state, running processes, and command history every time they disconnect.

The current architecture:

  • TerminalManager tracks sessions by session_id (UUID) in a dictionary
  • Each session creates a new PTY and docker exec process
  • WebSocket I/O loops are tied to the session lifecycle
  • No concept of reconnection or session persistence

Goals / Non-Goals

Goals:

  • Terminal sessions persist across WebSocket disconnections/reconnections
  • Users reconnect to the same shell process, maintaining state and history
  • Add explicit "Reset Terminal" functionality to kill and restart the session
  • Graceful handling of idle timeouts to clean up abandoned sessions
  • Buffer recent output for replay on reconnect

Non-Goals:

  • Multi-user shared terminal sessions (one session per instance, but only one active WebSocket at a time)
  • Session persistence across container restarts or instance stops
  • Full terminal scrollback history persistence (only recent buffer)
  • Automatic session restoration after instance restart

Decisions

1. Session tracking by instance_id

  • Rationale: One persistent session per tool instance is the simplest model
  • Alternative: Track by user_id + instance_id — rejected because it's overkill; users don't need multiple terminals to the same container
  • Trade-off: Only one user can have an active terminal at a time per instance

2. Detach WebSocket from session on disconnect

  • Rationale: Keep the docker exec process alive, just remove the WebSocket reference
  • Implementation: Session stores a list of WebSocket connections (initially just one)
  • On disconnect: remove WebSocket from session, don't kill process
  • On reconnect: attach new WebSocket to existing session

3. Circular buffer for output replay

  • Rationale: Users should see what happened while disconnected
  • Size: 10KB buffer (configurable) — enough for ~100 lines of typical output
  • Implementation: Buffer stores raw bytes, replayed on WebSocket attach

4. Reset terminal via WebSocket message

  • Rationale: Users need a way to kill a stuck or corrupted session
  • Implementation: JSON control message {"type": "reset"} kills process and starts fresh
  • Alternative: HTTP endpoint — rejected because it's more complex and less intuitive

5. Idle timeout cleanup

  • Rationale: Prevent resource leaks from abandoned sessions
  • Timeout: 30 minutes of no WebSocket connections
  • Implementation: Background task checks last_activity timestamp

Risks / Trade-offs

  • [Risk] Zombie sessions: Users disconnect and never reconnect, leaving docker exec processes running
    • Mitigation: Idle timeout of 30 minutes cleans up abandoned sessions
  • [Risk] Session corruption: If the shell process crashes, the session is dead but still tracked
    • Mitigation: Health check on docker exec process; auto-reset on next connect if dead
  • [Risk] Concurrent connections: Multiple tabs trying to connect to the same instance
    • Mitigation: Only allow one active WebSocket per session; new connection closes old one with a message

Migration Plan

  1. Deploy updated backend services (TerminalManager, TerminalSession, terminal endpoint)
  2. Deploy frontend changes (reset button, reconnection handling)
  3. No database migration needed
  4. Rollback: Revert to previous code; existing sessions will be killed on disconnect as before

Open Questions

  • Should we show a "session resumed" indicator in the UI?
  • Should we persist the last N commands for command history?