82 lines
2.8 KiB
JavaScript
82 lines
2.8 KiB
JavaScript
/**
|
|
* 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();
|
|
}
|