Files
SuperCharged-Claude-Code-Up…/SSE_ARCHITECTURE_DIAGRAMS.md
uroma 55aafbae9a Fix project isolation: Make loadChatHistory respect active project sessions
- Modified loadChatHistory() to check for active project before fetching all sessions
- When active project exists, use project.sessions instead of fetching from API
- Added detailed console logging to debug session filtering
- This prevents ALL sessions from appearing in every project's sidebar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 14:43:05 +00:00

30 KiB

SSE Architecture Diagrams

Overview Diagram

┌─────────────────────────────────────────────────────────────────┐
│                         Browser Client                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌──────────────┐         ┌──────────────┐                     │
│  │ Session UI   │         │ Terminal UI  │                     │
│  │              │         │              │                     │
│  │ ┌──────────┐ │         │ ┌──────────┐ │                     │
│  │ │ SSE      │ │         │ │ SSE      │ │                     │
│  │ │ Client   │ │         │ │ Client   │ │                     │
│  │ └────┬─────┘ │         │ └────┬─────┘ │                     │
│  └──────┼───────┘         └──────┼───────┘                     │
│         │                        │                              │
│         └────────┬───────────────┘                              │
│                  │                                              │
│                  ▼                                              │
│         ┌────────────────┐                                      │
│         │ REST API calls │ (POST prompt, GET status, etc.)      │
│         └────────────────┘                                      │
└───────────────────┬──────────────────────────────────────────────┘
                    │
                    │ HTTP/HTTPS
                    │
┌───────────────────▼──────────────────────────────────────────────┐
│                         nginx (Optional)                         │
├─────────────────────────────────────────────────────────────────┤
│  • Disable buffering for SSE                                     │
│  • Proxy to Node.js backend                                      │
│  • Long-lived connection support                                 │
└───────────────────┬──────────────────────────────────────────────┘
                    │
                    │
┌───────────────────▼──────────────────────────────────────────────┐
│                    Express Server (Node.js)                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │                     Routes Layer                          │  │
│  ├──────────────────────────────────────────────────────────┤  │
│  │                                                            │  │
│  │  /api/session/:id/events      → SSE streaming endpoint   │  │
│  │  /api/session/:id/prompt      → Send command             │  │
│  │  /api/session/:id/status      → Get status               │  │
│  │  /api/session/:id/context     → Get context              │  │
│  │  ...                                                         │  │
│  └───────────────────────┬──────────────────────────────────┘  │
│                          │                                       │
│                          ▼                                       │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │                  SSE Manager                              │  │
│  ├──────────────────────────────────────────────────────────┤  │
│  │  • Manages SSE connections                               │  │
│  │  • Handles heartbeat                                     │  │
│  │  • Broadcasts events to clients                          │  │
│  │  • Connection lifecycle management                       │  │
│  └───────────────────────┬──────────────────────────────────┘  │
│                          │                                       │
│                          ▼                                       │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │                   Event Bus                               │  │
│  ├──────────────────────────────────────────────────────────┤  │
│  │  • Pub/Sub event system                                  │  │
│  │  • Filters by session ID                                 │  │
│  │  • Tracks listeners and metrics                          │  │
│  │  • No memory leaks (proper cleanup)                      │  │
│  └───────────┬───────────────┬───────────────┬───────────────┘  │
│              │               │               │                   │
│              ▼               ▼               ▼                   │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │ Claude       │  │ Terminal     │  │ Approval     │          │
│  │ Service      │  │ Service      │  │ Manager      │          │
│  │              │  │              │  │              │          │
│  │ • Spawn PTY  │  │ • Manage     │  │ • Track      │          │
│  │ • Send cmd   │  │   terminals  │  │   requests   │          │
│  │ • Parse ops  │  │ • Buffer     │  │ • Timeouts   │          │
│  │              │  │   output     │  │              │          │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘          │
│         │                  │                  │                   │
│         └──────────────────┴──────────────────┘                  │
│                          │                                       │
│                          ▼                                       │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │              Claude Code Processes                        │  │
│  │  (one per session, managed by ClaudeService)              │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                   │
└───────────────────────────────────────────────────────────────────┘

Event Flow Diagram

User sends command:
┌──────────┐        ┌──────────┐       ┌──────────────┐
│  Client  │───────▶│   REST   │──────▶│ Claude       │
│          │  POST  │  API     │       │ Service      │
└──────────┘ /prompt│          │       └──────┬───────┘
                           /session/         │
                           :id/prompt        │
                                             ▼
                                    ┌──────────────┐
                                    │ Claude Code  │
                                    │ Process      │
                                    └──────┬───────┘
                                           │
                                           │ Output
                                           ▼
                                    ┌──────────────┐
                                    │ Claude       │
                                    │ Service      │
                                    └──────┬───────┘
                                           │
                                           │ emit('session-output')
                                           ▼
┌──────────┐                        ┌──────────────┐
│  Client  │◀───────────────────────│  Event Bus   │
│          │   SSE stream           │              │
└──────────┘                        └──────────────┘
     │                                    │
     │                                    │
     │       ┌────────────────────────────┘
     │       │
     ▼       ▼
┌──────────────────────────────────────────┐
│          Client Browser                  │
│  ┌────────────────────────────────────┐ │
│  │ EventSource receives event         │ │
│  │ event: session-output              │ │
│  │ data: {"content": "..."}           │ │
│  └────────────────────────────────────┘ │
│           │                              │
│           ▼                              │
│  ┌────────────────────────────────────┐ │
│  │ Update UI with output              │ │
│  └────────────────────────────────────┘ │
└──────────────────────────────────────────┘

Connection Lifecycle Diagram

Client                                Server
  │                                     │
  │  1. GET /api/session/:id/events    │
  │────────────────────────────────────▶│
  │                                     │  SSE Manager.addConnection()
  │                                     │  • Set headers
  │                                     │  • Flush response
  │                                     │  • Subscribe to events
  │                                     │
  │◀────────────────────────────────────│  2. Send "connected" event
  │  event: connected                   │
  │  data: {"sessionId": "..."}         │
  │                                     │
  │  3. POST /api/session/:id/prompt   │
  │────────────────────────────────────▶│
  │  {"command": "ls -la"}              │  ClaudeService.sendCommand()
  │                                     │
  │  4. Continue connection...          │
  │◀────────────────────────────────────│  EventBus emits 'session-output'
  │  event: session-output              │
  │  data: {"content": "file1.txt..."}  │
  │                                     │
  │◀────────────────────────────────────│  EventBus emits 'session-output'
  │  event: session-output              │
  │  data: {"content": "file2.txt..."}  │
  │                                     │
  │◀────────────────────────────────────│  :heartbeat (every 30s)
  │  :heartbeat                         │
  │                                     │
  │  5. Client disconnects              │
  │────────────────────────────────────▶│  req.on('close')
  │                                     │  • Unsubscribe from events
  │                                     │  • Stop heartbeat
  │                                     │  • Remove from tracking

Reconnection Logic Diagram

┌─────────────────────────────────────────────────────────────┐
│                     Client Reconnection                      │
└─────────────────────────────────────────────────────────────┘

Connection Lost
       │
       ▼
┌──────────────────┐
│ Is this a        │──── Yes ──▶ Emit 'disconnected'
│ permanent error? │              (404, session deleted)
└────────┬─────────┘
         │ No
         ▼
┌──────────────────┐
│ Reconnect        │
│ attempts <       │──── No ───▶ Stop reconnecting
│ max attempts?    │              Emit 'disconnected' (permanent)
└────────┬─────────┘
         │ Yes
         ▼
┌──────────────────┐
│ Calculate delay  │
│ with exponential │
│ backoff:         │
│ delay = min(     │
│   1000 * 1.5^attempts,│
│   30000          │
│ )                │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ Wait for delay   │
│ (1s, 1.5s, 2.25s,│
│  ... up to 30s)  │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ Emit 'reconnecting'│
│ { attempt,       │
│   delay,         │
│   maxAttempts }  │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ Create new       │
│ EventSource      │
│ connection       │
└────────┬─────────┘
         │
         └──────────────┐
                        │
         ┌──────────────┘
         │
         ▼
    Connection Open?
         │
    ┌────┴────┐
    │         │
   Yes       No
    │         │
    │         └─────────────────────────┐
    │                                   │
    ▼                                   ▼
┌─────────────┐                 Increment attempt count
│ Emit        │                 and loop back
│ 'connected' │
└─────────────┘

Architecture Comparison

Before (WebSocket - Current)

┌────────────────────────────────────────────────────────────┐
│                     WebSocket Flow                          │
├────────────────────────────────────────────────────────────┤
│                                                             │
│  Client                     Server                          │
│    │                          │                             │
│    │  1. Connect to ws://...  │                             │
│    ├─────────────────────────▶│                             │
│    │                          │  WebSocket Server           │
│    │                          │  • Parse session cookie      │
│    │                          │  • Accept connection        │
│    │                          │                             │
│    │◀─────────────────────────│  2. Send "connected"        │
│    │                          │                             │
│    │  3. Send "subscribe"     │                             │
│    │     message with         │                             │
│    │     sessionId            │                             │
│    ├─────────────────────────▶│  • Store client → session   │
│    │                          │    mapping                  │
│    │                          │                             │
│    │  4. Send "command"       │                             │
│    ├─────────────────────────▶│  • Route to ClaudeService   │
│    │                          │                             │
│    │                          │  ClaudeService emits        │
│    │                          │  • Find all clients         │
│    │◀─────────────────────────│    subscribed to session    │
│    │                          │  • Send to each client      │
│                                                             │
│  Problem: Session context is in message, not URL           │
│  Problem: Client must manage subscription state            │
│  Problem: Multiple tabs can have conflicting subscriptions │
└────────────────────────────────────────────────────────────┘

After (SSE - Proposed)

┌────────────────────────────────────────────────────────────┐
│                       SSE Flow                              │
├────────────────────────────────────────────────────────────┤
│                                                             │
│  Client                     Server                          │
│    │                          │                             │
│    │  1. GET /api/session/    │                             │
│    │     :id/events           │                             │
│    ├─────────────────────────▶│  • Validate session ID     │
│    │                          │  • Check session exists     │
│    │                          │  • Create SSE connection    │
│    │                          │  • Subscribe to all events  │
│    │                          │    for this session         │
│    │                          │                             │
│    │◀─────────────────────────│  2. Send "connected"        │
│    │                          │                             │
│    │  3. POST /api/session/   │                             │
│    │     :id/prompt           │                             │
│    ├─────────────────────────▶│  • Validate session ID      │
│    │     {command}            │  • Route to ClaudeService   │
│    │                          │                             │
│    │                          │  ClaudeService emits        │
│    │                          │  • EventBus broadcasts      │
│    │◀─────────────────────────│    to all subscribers       │
│    │                          │  • SSE Manager sends to     │
│    │                          │    connected clients        │
│                                                             │
│  Benefit: Session context in URL                            │
│  Benefit: Automatic subscription                            │
│  Benefit: Multiple tabs work correctly                      │
│  Benefit: Works through nginx with proper config            │
└────────────────────────────────────────────────────────────┘

Component Relationships

┌──────────────────────────────────────────────────────────────┐
│                    Component Dependencies                     │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌─────────────┐         ┌─────────────┐                   │
│  │   Routes    │────────▶│ Validation  │                   │
│  │             │         │ Middleware  │                   │
│  └──────┬──────┘         └─────────────┘                   │
│         │                                                      │
│         ├──▶ ┌─────────────┐         ┌─────────────┐        │
│         │    │  SSE        │────────▶│  Event Bus  │        │
│         │    │  Manager    │         │             │        │
│         │    └─────────────┘         └──────┬──────┘        │
│         │                                    │               │
│         │    ┌─────────────┐                │               │
│         └───▶│ Claude      │                │               │
│              │ Service     │                │               │
│              └─────────────┘                │               │
│                  │                           │               │
│                  └───────────────────────────┘               │
│                        (emits events)                         │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Key:
──▶  Calls/Uses
│   Dependency
└   Returns data

Data Flow: Command to Output

┌──────────────────────────────────────────────────────────────┐
│              Complete Flow: Command → Output                 │
└──────────────────────────────────────────────────────────────┘

1. USER ACTION
   User types "ls -la" in terminal UI and presses Enter

2. CLIENT SENDS COMMAND
   POST /api/session/session-123/prompt
   {
     "command": "ls -la"
   }

3. SERVER VALIDATES
   • validateSessionId middleware checks format
   • validateCommand middleware checks body
   • Checks session exists in ClaudeService

4. CLAUDE SERVICE PROCESSES
   claudeService.sendCommand(sessionId, "ls -la")
   • Writes to Claude Code process stdin
   • Returns immediately (response comes later)

5. SERVER RESPONDS
   HTTP 202 Accepted
   {
     "success": true,
     "message": "Command sent"
   }

6. CLAUDE CODE PROCESSES
   • Process runs command
   • Generates output

7. CLAUDE SERVICE RECEIVES OUTPUT
   claudeService.handleOutput(sessionId, "file1.txt\nfile2.txt")
   •
   ▼
   eventBus.emit('session-output', {
     sessionId: 'session-123',
     type: 'stdout',
     content: 'file1.txt\nfile2.txt',
     timestamp: 1234567890
   })

8. EVENT BUS DISTRIBUTES
   • Finds all subscribers to 'session-output' for session-123
   • Calls each subscriber's handler

9. SSE MANAGER SENDS
   For each SSE connection for session-123:
   res.write('event: session-output\n')
   res.write('data: {"type":"stdout","content":"..."}\n')
   res.write('\n')

10. CLIENT RECEIVES
    EventSource receives event
    eventSource.addEventListener('session-output', (e) => {
      const data = JSON.parse(e.data)
      console.log(data.content)  // "file1.txt\nfile2.txt"
    })

11. UI UPDATES
    Terminal displays output

Error Handling Flow

┌──────────────────────────────────────────────────────────────┐
│                   Error Handling Flow                        │
└──────────────────────────────────────────────────────────────┘

Error occurs in ClaudeService
         │
         ▼
eventBus.emit('session-error', {
  sessionId: 'session-123',
  error: 'Command failed',
  code: 'CMD_ERROR',
  recoverable: true
})
         │
         ▼
Event Bus distributes to subscribers
         │
         ├──▶ SSE Manager
         │    │
         │    ▼
         │  Send to client:
         │  event: session-error
         │  data: {"error":"...","code":"CMD_ERROR"}
         │
         └──▶ Other subscribers
              (logging, monitoring, etc.)

Client receives error
         │
         ▼
eventStream.on('error', (data) => {
  showErrorToUser(data.error)
  if (data.recoverable) {
    showRetryButton()
  }
})

These diagrams illustrate:

  1. Overall system architecture
  2. Event flow from client to server and back
  3. Connection lifecycle
  4. Reconnection logic
  5. Before/after comparison
  6. Component dependencies
  7. Complete command-to-output flow
  8. Error handling flow

Last Updated: 2025-01-21