@clduab11/gemini-flow
Version:
Revolutionary AI agent swarm coordination platform with Google Services integration, multimedia processing, and production-ready monitoring. Features 8 Google AI services, quantum computing capabilities, and enterprise-grade security.
1,075 lines (904 loc) • 28.3 kB
text/typescript
/**
* Session Manager
*
* Advanced session persistence and state management for Project Mariner
* with cross-session sharing, backup/restore, and intelligent state merging
*/
import { EventEmitter } from "events";
import { Logger } from "../../utils/logger.js";
import { safeImport } from "../../utils/feature-detection.js";
import {
SessionManager as ISessionManager,
SessionState,
TabState,
SessionConfig,
BrowserTab,
IntegrationBaseError,
} from "./types.js";
import { BaseIntegration, HealthStatus } from "../shared/types.js";
export interface SessionManagerConfig {
storage: SessionStorageConfig;
persistence: SessionPersistenceConfig;
sharing: SessionSharingConfig;
backup: SessionBackupConfig;
encryption: SessionEncryptionConfig;
}
export interface SessionStorageConfig {
provider: "memory" | "filesystem" | "database" | "cloud";
path?: string;
connectionString?: string;
options?: Record<string, any>;
}
export interface SessionPersistenceConfig {
autoSave: boolean;
saveInterval: number;
maxSessions: number;
retentionDays: number;
compression: boolean;
}
export interface SessionSharingConfig {
enabled: boolean;
crossUser: boolean;
permissions: SessionPermission[];
encryption: boolean;
}
export interface SessionPermission {
user: string;
rights: ("read" | "write" | "delete")[];
expiration?: Date;
}
export interface SessionBackupConfig {
enabled: boolean;
interval: number;
retention: number;
remote: boolean;
remoteUrl?: string;
}
export interface SessionEncryptionConfig {
enabled: boolean;
algorithm: "aes-256-gcm" | "chacha20-poly1305";
keyDerivation: "pbkdf2" | "scrypt";
keyRotation: boolean;
rotationInterval: number;
}
export interface SessionExportOptions {
includePasswords: boolean;
includePrivateData: boolean;
compression: boolean;
encryption: boolean;
format: "json" | "binary" | "encrypted";
}
export interface SessionImportOptions {
mergeStrategy: "replace" | "merge" | "preserve";
validateIntegrity: boolean;
autoDecrypt: boolean;
preserveTimestamps: boolean;
}
export interface SessionAnalytics {
sessionCount: number;
avgSessionDuration: number;
mostActiveHours: number[];
commonPatterns: SessionPattern[];
storageUsage: number;
backupStatus: BackupStatus;
}
export interface SessionPattern {
type: "navigation" | "form-filling" | "data-extraction";
frequency: number;
lastSeen: Date;
domains: string[];
actions: string[];
}
export interface BackupStatus {
lastBackup: Date;
backupCount: number;
totalSize: number;
health: "good" | "warning" | "error";
remoteSync: boolean;
}
export class SessionManager extends BaseIntegration implements ISessionManager {
private config: SessionManagerConfig;
private storage: SessionStorage;
private encryptor?: SessionEncryptor;
private backup?: SessionBackup;
private analytics: SessionAnalyticsEngine;
// Active sessions cache
private activeSessions: Map<string, SessionState> = new Map();
private sessionTimers: Map<string, NodeJS.Timeout> = new Map();
// Performance metrics
private sessionMetrics = {
sessionsCreated: 0,
sessionsLoaded: 0,
sessionsSaved: 0,
sessionsDeleted: 0,
backupsCreated: 0,
avgLoadTime: 0,
avgSaveTime: 0,
storageErrors: 0,
};
constructor(config: SessionManagerConfig) {
super({
id: "session-manager",
name: "Session Manager",
version: "1.0.0",
enabled: true,
dependencies: [],
features: {
persistence: config.persistence.autoSave,
sharing: config.sharing.enabled,
backup: config.backup.enabled,
encryption: config.encryption.enabled,
},
performance: {
maxConcurrentOperations: 10,
timeoutMs: 30000,
retryAttempts: 3,
cacheEnabled: true,
cacheTTLMs: 3600000,
metricsEnabled: true,
},
security: {
encryption: config.encryption.enabled,
validateOrigins: true,
allowedHosts: [],
tokenExpiration: 3600,
auditLogging: true,
},
storage: {
provider: "local",
encryption: config.encryption.enabled,
compression: config.persistence.compression,
},
});
this.config = config;
this.logger = new Logger("SessionManager");
this.storage = new SessionStorage(config.storage, this.logger);
this.analytics = new SessionAnalyticsEngine(this.logger);
if (config.encryption.enabled) {
this.encryptor = new SessionEncryptor(config.encryption, this.logger);
}
if (config.backup.enabled) {
this.backup = new SessionBackup(config.backup, this.logger);
}
}
async initialize(): Promise<void> {
try {
this.status = "initializing";
this.logger.info("Initializing Session Manager", {
storage: this.config.storage.provider,
encryption: this.config.encryption.enabled,
backup: this.config.backup.enabled,
});
// Initialize storage
await this.storage.initialize();
// Initialize encryption if enabled
if (this.encryptor) {
await this.encryptor.initialize();
}
// Initialize backup if enabled
if (this.backup) {
await this.backup.initialize();
}
// Start auto-save timer if enabled
if (this.config.persistence.autoSave) {
this.startAutoSave();
}
// Load existing sessions
await this.loadExistingSessions();
this.status = "ready";
this.logger.info("Session Manager initialized successfully");
this.emit("initialized", { timestamp: new Date() });
} catch (error) {
this.status = "error";
const sessionError = new IntegrationBaseError(
`Failed to initialize Session Manager: ${error.message}`,
"INIT_FAILED",
"SessionManager",
"critical",
false,
{ originalError: error.message },
);
this.emitError(sessionError);
throw sessionError;
}
}
async shutdown(): Promise<void> {
try {
this.logger.info("Shutting down Session Manager");
this.status = "shutdown";
// Save all active sessions
await this.saveAllActiveSessions();
// Clear timers
for (const timer of this.sessionTimers.values()) {
clearTimeout(timer);
}
this.sessionTimers.clear();
// Shutdown components
await this.storage.shutdown();
if (this.backup) {
await this.backup.shutdown();
}
this.logger.info("Session Manager shutdown complete");
this.emit("shutdown", { timestamp: new Date() });
} catch (error) {
this.logger.error("Error during Session Manager shutdown", error);
throw error;
}
}
async healthCheck(): Promise<HealthStatus> {
try {
// Check storage health
const storageHealth = await this.storage.healthCheck();
if (storageHealth === "critical") {
return "critical";
}
// Check backup health if enabled
if (this.backup) {
const backupHealth = await this.backup.healthCheck();
if (backupHealth === "critical") {
return "warning"; // Backup failure is not critical
}
}
// Check encryption if enabled
if (this.encryptor) {
const encryptionHealth = await this.encryptor.healthCheck();
if (encryptionHealth === "critical") {
return "critical";
}
}
return "healthy";
} catch (error) {
this.logger.error("Health check failed", error);
return "critical";
}
}
getMetrics(): Record<string, number> {
return {
...this.sessionMetrics,
activeSessions: this.activeSessions.size,
storageSize: this.storage.getStorageSize(),
analyticsData: this.analytics.getMetrics(),
};
}
// === SESSION MANAGEMENT METHODS ===
async saveSession(session: SessionState): Promise<void> {
const startTime = performance.now();
try {
this.logger.info("Saving session", { sessionId: session.id });
// Validate session
this.validateSession(session);
// Encrypt if configured
let processedSession = session;
if (this.encryptor) {
processedSession = await this.encryptor.encryptSession(session);
}
// Save to storage
await this.storage.saveSession(processedSession);
// Update cache
this.activeSessions.set(session.id, session);
// Update analytics
this.analytics.recordSessionSave(session);
// Update metrics
const duration = performance.now() - startTime;
this.sessionMetrics.sessionsSaved++;
this.sessionMetrics.avgSaveTime =
(this.sessionMetrics.avgSaveTime + duration) / 2;
this.logger.info("Session saved successfully", {
sessionId: session.id,
duration,
tabs: session.tabs.size,
});
this.emit("session_saved", { session, duration, timestamp: new Date() });
} catch (error) {
this.sessionMetrics.storageErrors++;
const saveError = new IntegrationBaseError(
`Failed to save session: ${error.message}`,
"SESSION_SAVE_FAILED",
"SessionManager",
"medium",
true,
{ sessionId: session.id },
);
this.emitError(saveError);
throw saveError;
}
}
async loadSession(sessionId: string): Promise<SessionState> {
const startTime = performance.now();
try {
this.logger.info("Loading session", { sessionId });
// Check cache first
if (this.activeSessions.has(sessionId)) {
this.logger.debug("Session found in cache", { sessionId });
return this.activeSessions.get(sessionId)!;
}
// Load from storage
let session = await this.storage.loadSession(sessionId);
if (!session) {
throw new Error(`Session not found: ${sessionId}`);
}
// Decrypt if configured
if (this.encryptor) {
session = await this.encryptor.decryptSession(session);
}
// Validate loaded session
this.validateSession(session);
// Add to cache
this.activeSessions.set(sessionId, session);
// Update analytics
this.analytics.recordSessionLoad(session);
// Update metrics
const duration = performance.now() - startTime;
this.sessionMetrics.sessionsLoaded++;
this.sessionMetrics.avgLoadTime =
(this.sessionMetrics.avgLoadTime + duration) / 2;
this.logger.info("Session loaded successfully", {
sessionId,
duration,
tabs: session.tabs.size,
});
this.emit("session_loaded", { session, duration, timestamp: new Date() });
return session;
} catch (error) {
this.sessionMetrics.storageErrors++;
const loadError = new IntegrationBaseError(
`Failed to load session: ${error.message}`,
"SESSION_LOAD_FAILED",
"SessionManager",
"medium",
true,
{ sessionId },
);
this.emitError(loadError);
throw loadError;
}
}
async restoreSession(sessionId: string): Promise<void> {
try {
this.logger.info("Restoring session", { sessionId });
// Load session
const session = await this.loadSession(sessionId);
// This would integrate with BrowserOrchestrator to recreate tabs
// For now, we'll emit an event that the orchestrator can listen to
this.emit("session_restore_requested", {
session,
timestamp: new Date(),
});
this.logger.info("Session restore requested", { sessionId });
} catch (error) {
const restoreError = new IntegrationBaseError(
`Failed to restore session: ${error.message}`,
"SESSION_RESTORE_FAILED",
"SessionManager",
"medium",
true,
{ sessionId },
);
this.emitError(restoreError);
throw restoreError;
}
}
async mergeSession(
sessionAId: string,
sessionBId: string,
): Promise<SessionState> {
try {
this.logger.info("Merging sessions", { sessionAId, sessionBId });
// Load both sessions
const [sessionA, sessionB] = await Promise.all([
this.loadSession(sessionAId),
this.loadSession(sessionBId),
]);
// Create merged session
const mergedSession: SessionState = {
id: `merged_${Date.now()}`,
tabs: new Map([...sessionA.tabs, ...sessionB.tabs]),
globalState: { ...sessionA.globalState, ...sessionB.globalState },
cookies: { ...sessionA.cookies, ...sessionB.cookies },
localStorage: { ...sessionA.localStorage, ...sessionB.localStorage },
sessionStorage: {
...sessionA.sessionStorage,
...sessionB.sessionStorage,
},
timestamp: new Date(),
version: sessionA.version,
};
// Save merged session
await this.saveSession(mergedSession);
this.logger.info("Sessions merged successfully", {
mergedId: mergedSession.id,
tabsA: sessionA.tabs.size,
tabsB: sessionB.tabs.size,
totalTabs: mergedSession.tabs.size,
});
this.emit("sessions_merged", {
sessionA,
sessionB,
mergedSession,
timestamp: new Date(),
});
return mergedSession;
} catch (error) {
const mergeError = new IntegrationBaseError(
`Failed to merge sessions: ${error.message}`,
"SESSION_MERGE_FAILED",
"SessionManager",
"medium",
true,
{ sessionAId, sessionBId },
);
this.emitError(mergeError);
throw mergeError;
}
}
async listSessions(): Promise<string[]> {
try {
const sessionIds = await this.storage.listSessions();
this.logger.debug("Listed sessions", { count: sessionIds.length });
return sessionIds;
} catch (error) {
const listError = new IntegrationBaseError(
`Failed to list sessions: ${error.message}`,
"SESSION_LIST_FAILED",
"SessionManager",
"low",
true,
);
this.emitError(listError);
throw listError;
}
}
async deleteSession(sessionId: string): Promise<void> {
try {
this.logger.info("Deleting session", { sessionId });
// Remove from storage
await this.storage.deleteSession(sessionId);
// Remove from cache
this.activeSessions.delete(sessionId);
// Clear timer if exists
const timer = this.sessionTimers.get(sessionId);
if (timer) {
clearTimeout(timer);
this.sessionTimers.delete(sessionId);
}
this.sessionMetrics.sessionsDeleted++;
this.logger.info("Session deleted successfully", { sessionId });
this.emit("session_deleted", { sessionId, timestamp: new Date() });
} catch (error) {
const deleteError = new IntegrationBaseError(
`Failed to delete session: ${error.message}`,
"SESSION_DELETE_FAILED",
"SessionManager",
"medium",
true,
{ sessionId },
);
this.emitError(deleteError);
throw deleteError;
}
}
async exportSession(
sessionId: string,
options?: SessionExportOptions,
): Promise<string> {
try {
this.logger.info("Exporting session", { sessionId, options });
// Load session
const session = await this.loadSession(sessionId);
// Apply export options
const exportData = this.prepareExportData(session, options);
// Serialize based on format
let result: string;
switch (options?.format || "json") {
case "json":
result = JSON.stringify(exportData, null, 2);
break;
case "binary":
result = Buffer.from(JSON.stringify(exportData)).toString("base64");
break;
case "encrypted":
if (!this.encryptor) {
throw new Error("Encryption not configured");
}
result = await this.encryptor.encryptString(
JSON.stringify(exportData),
);
break;
default:
throw new Error(`Unsupported export format: ${options?.format}`);
}
// Compress if requested
if (options?.compression) {
result = await this.compressString(result);
}
this.logger.info("Session exported successfully", {
sessionId,
format: options?.format || "json",
size: result.length,
});
this.emit("session_exported", {
sessionId,
format: options?.format,
timestamp: new Date(),
});
return result;
} catch (error) {
const exportError = new IntegrationBaseError(
`Failed to export session: ${error.message}`,
"SESSION_EXPORT_FAILED",
"SessionManager",
"medium",
true,
{ sessionId },
);
this.emitError(exportError);
throw exportError;
}
}
async importSession(
data: string,
options?: SessionImportOptions,
): Promise<SessionState> {
try {
this.logger.info("Importing session", { options });
let sessionData = data;
// Decompress if needed
if (this.isCompressed(data)) {
sessionData = await this.decompressString(data);
}
// Decrypt if needed
if (options?.autoDecrypt && this.encryptor) {
try {
sessionData = await this.encryptor.decryptString(sessionData);
} catch (error) {
// Data might not be encrypted
this.logger.debug("Data not encrypted or decryption failed", error);
}
}
// Parse session data
let importedData: any;
try {
// Try JSON first
importedData = JSON.parse(sessionData);
} catch (error) {
// Try base64 decode
try {
const decoded = Buffer.from(sessionData, "base64").toString("utf-8");
importedData = JSON.parse(decoded);
} catch (decodeError) {
throw new Error("Invalid session data format");
}
}
// Validate integrity if requested
if (options?.validateIntegrity) {
this.validateImportedSession(importedData);
}
// Convert to SessionState
const session = this.convertImportedSession(importedData, options);
// Handle merge strategy
if (options?.mergeStrategy === "merge") {
try {
const existingSession = await this.loadSession(session.id);
return await this.mergeSessionStates(existingSession, session);
} catch (error) {
// Session doesn't exist, proceed with import
}
}
// Save imported session
await this.saveSession(session);
this.logger.info("Session imported successfully", {
sessionId: session.id,
tabs: session.tabs.size,
});
this.emit("session_imported", { session, timestamp: new Date() });
return session;
} catch (error) {
const importError = new IntegrationBaseError(
`Failed to import session: ${error.message}`,
"SESSION_IMPORT_FAILED",
"SessionManager",
"medium",
true,
);
this.emitError(importError);
throw importError;
}
}
// === HELPER METHODS ===
private validateSession(session: SessionState): void {
if (!session.id || !session.version || !session.timestamp) {
throw new Error("Invalid session format");
}
if (!(session.tabs instanceof Map)) {
throw new Error("Invalid session tabs format");
}
}
private startAutoSave(): void {
const interval = this.config.persistence.saveInterval;
setInterval(async () => {
try {
await this.saveAllActiveSessions();
} catch (error) {
this.logger.error("Auto-save failed", error);
}
}, interval);
}
private async saveAllActiveSessions(): Promise<void> {
const savePromises = Array.from(this.activeSessions.values()).map(
(session) =>
this.saveSession(session).catch((error) =>
this.logger.warn(`Failed to save session ${session.id}`, error),
),
);
await Promise.all(savePromises);
}
private async loadExistingSessions(): Promise<void> {
try {
const sessionIds = await this.storage.listSessions();
this.logger.info("Found existing sessions", { count: sessionIds.length });
// Load recent sessions into cache
const recentSessions = sessionIds.slice(0, 10); // Load last 10 sessions
for (const sessionId of recentSessions) {
try {
await this.loadSession(sessionId);
} catch (error) {
this.logger.warn(`Failed to load session ${sessionId}`, error);
}
}
} catch (error) {
this.logger.error("Failed to load existing sessions", error);
}
}
private prepareExportData(
session: SessionState,
options?: SessionExportOptions,
): any {
const exportData = {
id: session.id,
tabs: Object.fromEntries(session.tabs),
globalState: session.globalState,
cookies: session.cookies,
localStorage: session.localStorage,
sessionStorage: session.sessionStorage,
timestamp: session.timestamp,
version: session.version,
};
// Remove sensitive data if not included
if (!options?.includePasswords) {
// Remove password fields from forms, etc.
// Implementation would scan for password-like fields
}
if (!options?.includePrivateData) {
// Remove private browsing data
// Implementation would filter private data
}
return exportData;
}
private validateImportedSession(data: any): void {
if (!data.id || !data.version || !data.timestamp) {
throw new Error("Invalid imported session format");
}
if (!data.tabs || typeof data.tabs !== "object") {
throw new Error("Invalid tabs data in imported session");
}
}
private convertImportedSession(
data: any,
options?: SessionImportOptions,
): SessionState {
const session: SessionState = {
id: data.id,
tabs: new Map(Object.entries(data.tabs || {})),
globalState: data.globalState || {},
cookies: data.cookies || {},
localStorage: data.localStorage || {},
sessionStorage: data.sessionStorage || {},
timestamp: options?.preserveTimestamps
? new Date(data.timestamp)
: new Date(),
version: data.version || "1.0",
};
return session;
}
private async mergeSessionStates(
sessionA: SessionState,
sessionB: SessionState,
): Promise<SessionState> {
return {
id: sessionA.id, // Keep original ID
tabs: new Map([...sessionA.tabs, ...sessionB.tabs]),
globalState: { ...sessionA.globalState, ...sessionB.globalState },
cookies: { ...sessionA.cookies, ...sessionB.cookies },
localStorage: { ...sessionA.localStorage, ...sessionB.localStorage },
sessionStorage: {
...sessionA.sessionStorage,
...sessionB.sessionStorage,
},
timestamp: new Date(),
version: sessionA.version,
};
}
private async compressString(data: string): Promise<string> {
// Implement compression (gzip, brotli, etc.)
return data; // Placeholder
}
private async decompressString(data: string): Promise<string> {
// Implement decompression
return data; // Placeholder
}
private isCompressed(data: string): boolean {
// Check if data is compressed
return false; // Placeholder
}
// === ANALYTICS METHODS ===
getAnalytics(): SessionAnalytics {
return this.analytics.getAnalytics();
}
getSessionPatterns(): SessionPattern[] {
return this.analytics.getPatterns();
}
// === BACKUP METHODS ===
async createBackup(): Promise<void> {
if (!this.backup) {
throw new Error("Backup not configured");
}
await this.backup.createBackup();
this.sessionMetrics.backupsCreated++;
}
async restoreFromBackup(backupId: string): Promise<void> {
if (!this.backup) {
throw new Error("Backup not configured");
}
await this.backup.restoreFromBackup(backupId);
}
getBackupStatus(): BackupStatus {
if (!this.backup) {
return {
lastBackup: new Date(0),
backupCount: 0,
totalSize: 0,
health: "error",
remoteSync: false,
};
}
return this.backup.getStatus();
}
}
// === SUPPORTING CLASSES ===
class SessionStorage {
private config: SessionStorageConfig;
private logger: Logger;
private storageSize = 0;
constructor(config: SessionStorageConfig, logger: Logger) {
this.config = config;
this.logger = logger;
}
async initialize(): Promise<void> {
this.logger.info("Session storage initialized", {
provider: this.config.provider,
});
}
async shutdown(): Promise<void> {
this.logger.info("Session storage shutdown");
}
async healthCheck(): Promise<HealthStatus> {
return "healthy";
}
async saveSession(session: SessionState): Promise<void> {
// Implement storage-specific saving
this.storageSize += 1000; // Placeholder
}
async loadSession(sessionId: string): Promise<SessionState | null> {
// Implement storage-specific loading
return null; // Placeholder
}
async deleteSession(sessionId: string): Promise<void> {
// Implement storage-specific deletion
}
async listSessions(): Promise<string[]> {
// Implement storage-specific listing
return []; // Placeholder
}
getStorageSize(): number {
return this.storageSize;
}
}
class SessionEncryptor {
private config: SessionEncryptionConfig;
private logger: Logger;
constructor(config: SessionEncryptionConfig, logger: Logger) {
this.config = config;
this.logger = logger;
}
async initialize(): Promise<void> {
this.logger.info("Session encryptor initialized");
}
async healthCheck(): Promise<HealthStatus> {
return "healthy";
}
async encryptSession(session: SessionState): Promise<SessionState> {
// Implement session encryption
return session; // Placeholder
}
async decryptSession(session: SessionState): Promise<SessionState> {
// Implement session decryption
return session; // Placeholder
}
async encryptString(data: string): Promise<string> {
// Implement string encryption
return data; // Placeholder
}
async decryptString(data: string): Promise<string> {
// Implement string decryption
return data; // Placeholder
}
}
class SessionBackup {
private config: SessionBackupConfig;
private logger: Logger;
constructor(config: SessionBackupConfig, logger: Logger) {
this.config = config;
this.logger = logger;
}
async initialize(): Promise<void> {
this.logger.info("Session backup initialized");
}
async shutdown(): Promise<void> {
this.logger.info("Session backup shutdown");
}
async healthCheck(): Promise<HealthStatus> {
return "healthy";
}
async createBackup(): Promise<void> {
this.logger.info("Creating session backup");
}
async restoreFromBackup(backupId: string): Promise<void> {
this.logger.info("Restoring from backup", { backupId });
}
getStatus(): BackupStatus {
return {
lastBackup: new Date(),
backupCount: 0,
totalSize: 0,
health: "good",
remoteSync: this.config.remote,
};
}
}
class SessionAnalyticsEngine {
private logger: Logger;
private patterns: SessionPattern[] = [];
private analytics: SessionAnalytics;
constructor(logger: Logger) {
this.logger = logger;
this.analytics = {
sessionCount: 0,
avgSessionDuration: 0,
mostActiveHours: [],
commonPatterns: [],
storageUsage: 0,
backupStatus: {
lastBackup: new Date(),
backupCount: 0,
totalSize: 0,
health: "good",
remoteSync: false,
},
};
}
recordSessionSave(session: SessionState): void {
this.analytics.sessionCount++;
}
recordSessionLoad(session: SessionState): void {
// Record analytics
}
getAnalytics(): SessionAnalytics {
return this.analytics;
}
getPatterns(): SessionPattern[] {
return this.patterns;
}
getMetrics(): number {
return this.patterns.length;
}
}