## 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?
