Files
shopify-ai-backup/SESSION_CONTINUITY_FIX.md

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