110 lines
5.6 KiB
Markdown
110 lines
5.6 KiB
Markdown
# Session Continuity Fix Summary
|
|
|
|
## Problem
|
|
The app was creating new OpenCode sessions for each message instead of reusing existing session, breaking conversation continuity. This affected:
|
|
1. **Continue when error** - When a message failed and was retried with a fallback model
|
|
2. **Messages in the same chat** - Each subsequent message created a new OpenCode session instead of continuing in the existing one
|
|
3. **Builder proceed with build** - Build messages created new sessions
|
|
4. **Redo operations** - Redoing a message didn't preserve the original session
|
|
|
|
## Root Causes
|
|
|
|
1. **Client-side missing opencodeSessionId**: The frontend (`builder.js`, `app.js`) wasn't sending `opencodeSessionId` in the message payload when creating new messages.
|
|
|
|
2. **Server-side overwriting session IDs**: The server's `ensureOpencodeSession()` function was creating new OpenCode session IDs even when one already existed, if validation failed or for certain edge cases.
|
|
|
|
3. **Session detection overwriting**: When a new OpenCode session was auto-created by the CLI, code was overwriting the existing `initialOpencodeSessionId`.
|
|
|
|
## Fixes Applied
|
|
|
|
### 1. Client-side (builder.js)
|
|
- **sendMessage()**: Added logic to preserve `session.opencodeSessionId` in message payload when sending messages
|
|
- **executeBuild()**: Added logic to preserve `session.opencodeSessionId` when starting a build process
|
|
- **redoProceedWithBuild()**: Added logic to preserve `session.opencodeSessionId` when redoing a build
|
|
- **triggerFallback()**: Enhanced to prioritize message's `opencodeSessionId` over session's (in case message has more recent session)
|
|
|
|
### 2. Client-side (app.js)
|
|
- **sendMessage()**: Added logic to preserve `session.opencodeSessionId` in message payload for regular chat UI
|
|
|
|
### 3. Server-side (server.js)
|
|
- **ensureOpencodeSession()**:
|
|
- When session validation fails, now returns the existing `session.opencodeSessionId` instead of creating a new one
|
|
- This prevents creating new sessions when CLI operations timeout or fail temporarily
|
|
|
|
- **createOpencodeSession() section**:
|
|
- Only sets `initialOpencodeSessionId` if not already set
|
|
- This prevents overwriting a locked initial session with a new auto-generated one
|
|
|
|
- **handleNewMessage()**:
|
|
- Explicitly logs when using `opencodeSessionId` from request body vs inheriting from session
|
|
- Ensures both paths are properly tracked
|
|
|
|
- **processMessage()**:
|
|
- Uses `message.opencodeSessionId` if explicitly provided in the request
|
|
- Falls back to `ensureOpencodeSession()` only if no explicit session ID
|
|
- This ensures retried/continued messages use the same session
|
|
|
|
- **sendToOpencode()**:
|
|
- Only updates session ID from captured events if no explicit session was passed
|
|
- Prevents overwriting a passed `--session` arg with an auto-generated one
|
|
|
|
- **Session recovery section**:
|
|
- Only sets `opencodeSessionId` to the detected ID if no session ID already exists
|
|
- Preserves `initialOpencodeSessionId` and uses it as the final session ID
|
|
- Added logging for both scenarios (new initial session detected vs preserving existing)
|
|
|
|
## How It Works Now
|
|
|
|
### First Message in a Session
|
|
1. Client sends message without `opencodeSessionId`
|
|
2. Server's `processMessage()` calls `ensureOpencodeSession()`
|
|
3. `ensureOpencodeSession()` creates a new OpenCode session (or lets CLI create one)
|
|
4. CLI reports session ID in JSON events
|
|
5. Server captures and stores it as `session.opencodeSessionId` and `session.initialOpencodeSessionId`
|
|
6. Session is now locked for all future messages
|
|
|
|
### Subsequent Messages
|
|
1. Client sends message **with** `opencodeSessionId` from `session.opencodeSessionId`
|
|
2. Server's `processMessage()` uses the explicit `message.opencodeSessionId`
|
|
3. `sendToOpencode()` is called with the explicit session ID
|
|
4. CLI runs with `--session <session-id>` argument
|
|
5. Same OpenCode session is used, maintaining conversation continuity
|
|
|
|
### Continue When Error / Retry
|
|
1. Original message fails, triggering `triggerModelFallback()`
|
|
2. Fallback creates a new message with `isContinuation: true`
|
|
3. New message includes the **original message's `opencodeSessionId`** in payload
|
|
4. Server uses the explicit session ID, continuing in the same OpenCode session
|
|
5. Conversation context is preserved across the error
|
|
|
|
### Redo Operations
|
|
1. **Proceed-with-build redo**: Uses `redoProceedWithBuild()` which preserves `session.opencodeSessionId`
|
|
2. **Regular opencode redo**: Uses `/api/sessions/:id/messages/:id/redo` endpoint which uses `session.opencodeSessionId`
|
|
3. Both maintain the same OpenCode session
|
|
|
|
## Key Changes
|
|
|
|
### Session ID Preservation
|
|
- **Never overwrite** `initialOpencodeSessionId` once it's set
|
|
- **Prefer explicit session IDs** from messages over auto-detected ones
|
|
- **Preserve across all operations**: send, redo, retry, continue
|
|
|
|
### Validation Failure Handling
|
|
- **Don't create new session** when validation fails
|
|
- **Return existing session** and let CLI handle it
|
|
- **Log extensively** for debugging session continuity issues
|
|
|
|
## Testing Checklist
|
|
- [ ] Send first message in a new session - should create new OpenCode session
|
|
- [ ] Send second message in same session - should reuse existing session
|
|
- [ ] Message fails and auto-retries - should continue in same session
|
|
- [ ] Click "Redo" on a message - should use same session
|
|
- [ ] Click "Proceed with Build" - should use same session
|
|
- [ ] Click "Redo Build" - should use same session
|
|
- [ ] Refresh page and send new message - should use existing session
|
|
|
|
## Files Modified
|
|
1. `chat/public/builder.js` - Client-side builder UI
|
|
2. `chat/public/app.js` - Client-side regular chat UI
|
|
3. `chat/server.js` - Server-side session management
|