# Session Persistence Design

## Philosophy: Client-Side as Source of Truth

### Core Principle

**The UI is everything.** What you see is what actually is. We don't want unexpected state mismatches between client and server.

### Design Decision

- **Session State (conversation)**: Client-side is source of truth
- **Final Results (completed tasks)**: Server-side for polling recovery

### Why Client-Side for Session State?

1. **No State Synchronization Problems**
   - Server storing session = two sources of truth
   - Client localStorage + Server disk = potential conflicts
   - Which is correct if they disagree?
   - Solution: Only one source of truth (client)

2. **The UI is Everything**
   - User sees the conversation in their browser
   - That's the reality of the session
   - Server doesn't need to "remember" conversations
   - Client owns what they see

3. **Simplicity and Elegance**
   - Server stays stateless
   - No disk I/O on every request
   - No cleanup/TTL management for conversations
   - Scales infinitely

4. **Client Control**
   - User controls their conversation data
   - Clear in browser localStorage
   - No server storage costs
   - Privacy-friendly

### Why Server-Side for Results?

The **only** reason server saves anything is for **long-running tasks**:

```
User sends request → Disconnects (browser closed, network issue)
Server continues processing (10 minutes)
Server finishes → Saves result to .co/session_results.jsonl
User comes back → Polls /sessions/{id} → Gets result
```

This is **NOT** for conversation state. It's only for:
- Completed task results
- Long-running operations
- Polling recovery when client disconnected

### Architecture

```
┌─────────────────────────────────┐
│ Client (localStorage)           │
│ - Session state (messages)      │
│ - Conversation context          │
│ - Turn count                    │
│ ✓ SOURCE OF TRUTH               │
└─────────────────────────────────┘
         ↓ sends with request
┌─────────────────────────────────┐
│ Server (stateless)              │
│ - Receives session from client  │
│ - Processes with agent          │
│ - Returns updated session       │
│ - Does NOT save to disk         │
└─────────────────────────────────┘
         ↓ only on completion
┌─────────────────────────────────┐
│ .co/session_results.jsonl       │
│ - Final results only            │
│ - For polling recovery          │
│ - NOT for conversation state    │
└─────────────────────────────────┘
```

### Implementation

#### TypeScript SDK (connectonion-ts/)
1. Save full `session` object to `localStorage` when received from server
2. Load `session` from `localStorage` on next request
3. Send session with every INPUT message
4. Server uses it, returns updated session, client saves again

#### Python SDK (connectonion/)
1. Accept `session` parameter in requests (already works)
2. Do NOT save conversation state to disk
3. Only save final results to `.co/session_results.jsonl` (already works)
4. Server remains completely stateless for conversations

### What Stays in .co/session_results.jsonl

```json
{
  "session_id": "abc-123",
  "status": "done",
  "prompt": "Original user request",
  "result": "Final answer after 10 minutes",
  "created": 1234567890,
  "expires": 1234654290,
  "duration_ms": 600000
}
```

**Notice:** No `messages`, no `context`, no conversation state. Just the final result.

### Benefits

1. **Single Source of Truth**: Client localStorage, no conflicts
2. **Stateless Server**: Scales infinitely, no disk I/O per request
3. **Simple Mental Model**: "What you see is what is"
4. **No Synchronization**: Client controls, server processes
5. **Clear Separation**:
   - Session state = client responsibility
   - Final results = server convenience (for polling)

### Edge Cases

**Q: What if user clears localStorage?**
A: Conversation resets. That's expected behavior - they cleared their data.

**Q: What if user switches devices?**
A: New conversation. Session is local to that browser. (Could add cloud sync later if needed, but not in scope)

**Q: What if server has a different session state?**
A: Impossible - server doesn't store session state, only final results.

**Q: What about Python SDK (server-side agents)?**
A: Session is in-memory during WebSocket connection. If connection drops, client (TypeScript) has session in localStorage and sends it back on reconnect.

### Migration Notes

**Current behavior** (already mostly correct):
- ✅ Server accepts `session` in requests
- ✅ Server returns updated `session` in responses
- ✅ Server saves final results to `.co/session_results.jsonl`
- ❌ TypeScript SDK doesn't save full session to localStorage (only session_id)

**What needs to change:**
- Only TypeScript SDK: Save full `session` object to localStorage (not just ID)
- Everything else stays the same

### Conclusion

**Client-side as source of truth** is the simplest, most elegant solution:
- No state synchronization issues
- Server stays stateless and scalable
- Clear ownership (client owns conversation, server provides results)
- UI is everything - what you see is what actually is

The `.co/session_results.jsonl` file serves a single, clear purpose: **enable polling for long-running task results**. It is NOT for conversation state management.
