Files
shopify-ai-backup/SESSION_CONTINUITY_FIX.md

5.6 KiB

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