Overview

The Session Service is the central coordinator for user session management in MentraOS Cloud. It handles session creation, retrieval, and transformation while delegating specialized tasks to the UserSession class and its managers. File: packages/cloud/src/services/session/session.service.ts

Key Responsibilities

  1. Session Lifecycle Management: Creating and retrieving sessions
  2. Message Routing: Relaying messages between glasses and apps
  3. Settings Management: User and app-specific settings
  4. Session Transformation: Preparing session data for clients
  5. Audio Routing: Directing audio data to appropriate handlers

Session Creation

The service creates or retrieves sessions with automatic reconnection handling:
async createSession(ws: WebSocket, userId: string): Promise<{ 
  userSession: UserSession, 
  reconnection: boolean 
}> {
  // Check for existing session
  const existingSession = UserSession.getById(userId);
  
  if (existingSession) {
    // Reconnection scenario
    existingSession.updateWebSocket(ws);
    existingSession.disconnectedAt = null;
    
    // Clear cleanup timer
    if (existingSession.cleanupTimerId) {
      clearTimeout(existingSession.cleanupTimerId);
      existingSession.cleanupTimerId = undefined;
    }
    
    return { userSession: existingSession, reconnection: true };
  }
  
  // Create new session
  const userSession = new UserSession(userId, ws);
  
  // Fetch and populate installed apps
  const installedApps = await appService.getAllApps(userId);
  for (const app of installedApps) {
    userSession.installedApps.set(app.packageName, app);
  }
  
  return { userSession: userSession, reconnection: false };
}

Reconnection Handling

When a user reconnects:
  • Existing session is preserved
  • WebSocket connection is updated
  • Cleanup timers are cancelled
  • Disconnection timestamp is cleared

Session Retrieval

Multiple methods for accessing sessions:
// By session ID
getSession(sessionId: string): UserSession | null

// By user ID (email)
getSessionByUserId(userId: string): UserSession | null

// All active sessions
getAllSessions(): UserSession[]

Session Transformation

Prepares session data for client consumption with calculated requirements:
async transformUserSessionForClient(userSession: UserSession): Promise<any> {
  // Collect app subscriptions
  const appSubscriptions: Record<string, string[]> = {};
  for (const packageName of userSession.runningApps) {
    appSubscriptions[packageName] = subscriptionService.getAppSubscriptions(
      userId, 
      packageName
    );
  }
  
  // Calculate stream requirements
  const hasPCMTranscriptionSubscriptions = 
    subscriptionService.hasPCMTranscriptionSubscriptions(userId);
  const requiresAudio = hasPCMTranscriptionSubscriptions.hasMedia;
  
  // Update microphone state based on requirements
  const requiredData = userSession.microphoneManager.calculateRequiredData(
    hasPCMTranscriptionSubscriptions.hasPCM, 
    hasPCMTranscriptionSubscriptions.hasTranscription
  );
  userSession.microphoneManager.updateState(requiresAudio, requiredData);
  
  return {
    userId,
    startTime: userSession.startTime,
    activeAppSessions: Array.from(userSession.runningApps),
    loadingApps: Array.from(userSession.loadingApps),
    appSubscriptions,
    requiresAudio,
    minimumTranscriptionLanguages,
    isTranscribing: userSession.isTranscribing || false
  };
}

Message Routing

Relay to Apps

Routes messages from glasses to subscribed apps:
relayMessageToApps(userSession: UserSession, data: GlassesToCloudMessage): void {
  // Get subscribed apps
  const subscribedPackageNames = subscriptionService.getSubscribedApps(
    userSession, 
    data.type
  );
  
  // Send to each subscriber
  for (const packageName of subscribedPackageNames) {
    const connection = userSession.appWebsockets.get(packageName);
    
    if (connection && connection.readyState === WebSocket.OPEN) {
      const dataStream: DataStream = {
        type: CloudToAppMessageType.DATA_STREAM,
        sessionId: `${userSession.sessionId}-${packageName}`,
        streamType: data.type as StreamType,
        data,
        timestamp: new Date()
      };
      
      connection.send(JSON.stringify(dataStream));
    }
  }
}

Audio Routing

Delegates audio processing to the AudioManager:
relayAudioToApps(userSession: UserSession, audioData: ArrayBuffer): void {
  userSession.audioManager.processAudioData(audioData, false);
}

Audio Response Routing

Routes audio play responses back to requesting apps:
relayAudioPlayResponseToApp(userSession: UserSession, audioResponse: any): void {
  const requestId = audioResponse.requestId;
  
  // Find which app made the request
  const packageName = userSession.audioPlayRequestMapping.get(requestId);
  
  if (packageName) {
    const connection = userSession.appWebsockets.get(packageName);
    // Send response to app...
  }
}

Settings Management

User Settings

Retrieves combined system and app settings:
async getUserSettings(userId: string): Promise<Record<string, any>> {
  const user = await User.findOne({ email: userId });
  
  if (!user) {
    return DEFAULT_AUGMENTOS_SETTINGS;
  }
  
  // Get system settings
  const augmentosSettings = user.getAugmentosSettings();
  
  // Add app settings
  const allSettings = {
    ...augmentosSettings,
    appSettings: Object.fromEntries(user.appSettings || new Map())
  };
  
  return allSettings;
}

App-Specific Settings

async getAppSettings(userId: string, packageName: string): Promise<Record<string, any>> {
  const allSettings = await this.getUserSettings(userId);
  return allSettings.appSettings?.[packageName] || {};
}

Default Settings

System default settings when user settings are not available:
const DEFAULT_AUGMENTOS_SETTINGS = {
  useOnboardMic: false,
  contextualDashboard: true,
  headUpAngle: 20,
  brightness: 50,
  autoBrightness: false,
  sensingEnabled: true,
  alwaysOnStatusBar: false,
  bypassVad: false,
  bypassAudioEncoding: false,
  metricSystemEnabled: false
};

Error Handling

  • Session Creation: Logs errors and re-throws for upstream handling
  • Message Routing: Catches and logs send errors per app
  • Settings Retrieval: Returns defaults on error
  • Transformation: Returns minimal data on error

Integration Points

  • UserSession: Core session object creation and management
  • SubscriptionService: Determining app subscriptions and requirements
  • AppService: Fetching installed apps
  • User Model: Database access for settings
  • AudioManager: Audio data processing
  • MicrophoneManager: Microphone state management

Best Practices

  1. Always check WebSocket state before sending messages
  2. Handle reconnections gracefully by preserving session state
  3. Return sensible defaults when data is unavailable
  4. Log errors with context for debugging
  5. Delegate specialized tasks to appropriate managers