# 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 ` 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