| /** | |
| * Session Manager for Antigravity Cloud Code | |
| * | |
| * Handles session ID generation and caching for prompt caching continuity. | |
| * Mimics the Antigravity binary behavior: generates a session ID at startup | |
| * and keeps it for the process lifetime, scoped per account/connection. | |
| * | |
| * Reference: antigravity-claude-proxy/src/cloudcode/session-manager.js | |
| */ | |
| import crypto from "crypto"; | |
| import { MEMORY_CONFIG } from "../config/runtimeConfig.js"; | |
| // Runtime storage: Key = connectionId, Value = { sessionId, lastUsed } | |
| const runtimeSessionStore = new Map(); | |
| // Periodically evict entries that haven't been used within TTL | |
| const cleanupInterval = setInterval(() => { | |
| const now = Date.now(); | |
| for (const [key, entry] of runtimeSessionStore) { | |
| if (now - entry.lastUsed > MEMORY_CONFIG.sessionTtlMs) { | |
| runtimeSessionStore.delete(key); | |
| } | |
| } | |
| }, MEMORY_CONFIG.sessionCleanupIntervalMs); | |
| // Allow Node.js to exit even if interval is still active | |
| if (cleanupInterval.unref) cleanupInterval.unref(); | |
| /** | |
| * Get or create a session ID for the given connection. | |
| * | |
| * The binary generates a session ID once at startup: `rs() + Date.now()`. | |
| * Since 9router is long-running, we simulate this "per-launch" behavior by | |
| * storing a generated ID in memory for each connection. | |
| * | |
| * - If 9router restarts, the ID changes (matching binary restart behavior). | |
| * - Within a running instance, the ID is stable for that connection. | |
| * - This enables prompt caching while using the EXACT random logic of the binary. | |
| * | |
| * @param {string} connectionId - The connection identifier (email or unique ID) | |
| * @returns {string} A stable session ID string matching binary format | |
| */ | |
| export function deriveSessionId(connectionId) { | |
| if (!connectionId) { | |
| return generateBinaryStyleId(); | |
| } | |
| const existing = runtimeSessionStore.get(connectionId); | |
| if (existing) { | |
| existing.lastUsed = Date.now(); | |
| return existing.sessionId; | |
| } | |
| // Evict oldest entry if store exceeds max size (safety cap between cleanup cycles) | |
| const MAX_SESSIONS = 1000; | |
| if (runtimeSessionStore.size >= MAX_SESSIONS) { | |
| const oldest = runtimeSessionStore.keys().next().value; | |
| runtimeSessionStore.delete(oldest); | |
| } | |
| const sessionId = generateBinaryStyleId(); | |
| runtimeSessionStore.set(connectionId, { sessionId, lastUsed: Date.now() }); | |
| return sessionId; | |
| } | |
| /** | |
| * Generate a Session ID using the binary's exact logic. | |
| * Format: `rs() + Date.now()` where `rs()` is randomUUID | |
| * | |
| * @returns {string} A session ID in binary format | |
| */ | |
| export function generateBinaryStyleId() { | |
| return crypto.randomUUID() + Date.now().toString(); | |
| } | |
| /** | |
| * Clears all session IDs (e.g. useful for testing or explicit reset) | |
| */ | |
| export function clearSessionStore() { | |
| runtimeSessionStore.clear(); | |
| } | |