WebSocket Protocol
The chat widget communicates with the Durable Object via WebSocket.
Connection
GET /ws/:sessionId
Upgrade: websocketThe worker proxies the upgrade request to the Durable Object identified by sessionId.
Message Types
Client → Server (WSClientMessage)
typescript
type WSClientMessage =
| { type: 'message'; text: string } // Regular chat message
| { type: 'quick_reply'; text: string } // Quick reply button tap
| { type: 'escalate'; name: string; email: string } // Switch to live modeServer → Client (WSServerMessage)
typescript
type WSServerMessage =
| { type: 'message'; sender: 'visitor' | 'anshul'; text: string; timestamp: number }
| { type: 'bot_message'; text: string; quickReplies?: string[] }
| { type: 'session_started'; sessionId: string; mode: 'bot' | 'live' }
| { type: 'session_ended'; reason: 'timeout' | 'closed' }
| { type: 'request_info' }
| { type: 'typing' }
| { type: 'message_limit'; message: string }
| { type: 'away'; message: string }
| { type: 'error'; message: string }Session Modes
Bot Mode
- On WebSocket connect, server sends
session_startedwithmode: 'bot' - Server sends
bot_messagewith the knowledge base greeting - Client sends
messageorquick_reply— server responds withbot_message - Bot responses may include
quickRepliesarray for suggested follow-ups - Session times out after 10 minutes of inactivity
Live Mode
- Client sends
escalatewith name and email - A Slack thread is created for the session
- Server switches mode to
live - Client
message→ posted to Slack thread - Slack replies → relayed to client as
messagewithsender: 'anshul' - Session times out after 30 minutes of inactivity
Rate Limits
| Constraint | Value |
|---|---|
| Per-message rate limit | 2 seconds between messages |
| Max message length | 2000 characters |
| Message limit | Enforced via message_count in storage |
Messages exceeding 2000 characters receive an error response before any processing.
Timeouts and Alarms
The Durable Object uses Cloudflare alarms for timeout management:
| Timeout | Duration | Behavior |
|---|---|---|
| Bot inactivity | 10 minutes | Sends session_ended with reason timeout |
| Live inactivity | 30 minutes | Sends session_ended with reason timeout |
| Away indicator | 5 minutes | Sends away message if Anshul hasn't replied |
State Machine
┌───────────────────────┐
│ CONNECTED │
│ mode: 'bot' │
│ (Gemini answers) │
└───────────┬───────────┘
│ escalate
▼
┌───────────────────────┐
│ CONNECTED │
│ mode: 'live' │
│ (Slack relay) │
└───────────┬───────────┘
│ timeout / close
▼
┌───────────────────────┐
│ ENDED │
│ session_ended sent │
└───────────────────────┘Storage Keys (Durable Object)
| Key | Type | Purpose |
|---|---|---|
session_state | SessionState | Visitor info, mode, Slack thread |
last_visitor_message | number | Timestamp for inactivity tracking |
last_anshul_reply | number | Timestamp for away indicator |
last_unmatched_text | string | Last bot question to forward on escalation |
session_id | string | Session identifier |
conversation_history | ConversationHistory | AI conversation context |
message_count | number | Total messages in session |