// agentSSEState.ts — P3-5: stato condiviso estratto da agentSSE.ts // Esportato come modulo puro — zero importazioni da agentSSE.ts (no circular deps). // Importato da agentSSE.ts (lifecycle) e agentSSEDispatch.ts (handleEvent). // ─── Backoff schedule (delegato a USR) ──────────────────────────────────────── export const BACKOFF_MS = [1000, 2000, 4000, 8000, 16000, 30000]; // S346: timeout globale task SSE — se non completa in MAX_TASK_DURATION_MS, // il task viene automaticamente cancellato con status ERROR. export const MAX_TASK_DURATION_MS = 8 * 60 * 1000; // S701: 5→8min — MAX_ITER=16 + browser testing // S399-GUARD: max task SSE concorrenti. export const MAX_CONCURRENT_SSE = 1; // ─── Params storage per retry/queue promotion ───────────────────────────────── export interface TaskParams { goal: string; context: Array<{ role: string; content: string }>; maxSteps: number; resumeFromStep?: number; // P16-F3: propagato da agente:promote-queued per resume corretto persona?: string; // P17-F5: expertise persona ("researcher"|"coder"|"architect"|"reasoner"|"analyst") } export const _taskParams = new Map(); // S345: track task già avviati sul backend — evita double-POST su reconnect USR. export const _taskStarted = new Set(); // ─── TTFT tracking ──────────────────────────────────────────────────────────── export const _connectAt = new Map(); // S420: streaming text buffer — accumula token per task attivo // S758-P4.1: thinking timer — fires if no step_start within 3s after task_start export const _thinkingTimers: Map> = new Map(); export const _streamingBuffer = new Map(); // S422: TTL timestamps — previene memory leak su connessioni SSE interrotte senza task_done export const _bufferTimestamps = new Map(); const _BUFFER_TTL_MS = 5 * 60 * 1000; // 5 minuti if (typeof window !== "undefined") { setInterval(() => { const _now = Date.now(); _bufferTimestamps.forEach((ts, id) => { if (_now - ts > _BUFFER_TTL_MS) { _streamingBuffer.delete(id); _bufferTimestamps.delete(id); } }); }, 2 * 60 * 1000); } // S346: timer ID per task timeout — clearTimeout su cancel/done/error export const _taskTimers = new Map>(); // A2-retry: tentativi residui di riavvio automatico dopo idle > 10min. // P1-FIX (S493): persistiti in sessionStorage — sopravvivono al hard reload. // Esposti come container mutabile per evitare ES module binding assignment error. const _IDLE_RETRIES_KEY = "agente_idle_retries"; function _loadIdleRetries(): number { try { const s = sessionStorage.getItem(_IDLE_RETRIES_KEY); return s !== null ? Math.max(0, Math.min(2, parseInt(s, 10) || 0)) : 2; } catch { return 2; } } export function _saveIdleRetries(n: number): void { try { sessionStorage.setItem(_IDLE_RETRIES_KEY, String(n)); } catch { /* non-blocking */ } } // P5-FIX (S497): recovery retry counter const _RECOVERY_RETRIES_KEY = "agente_recovery_retries"; function _loadRecoveryRetries(): number { try { const s = sessionStorage.getItem(_RECOVERY_RETRIES_KEY); return s !== null ? Math.max(0, Math.min(2, parseInt(s, 10) || 0)) : 2; } catch { return 2; } } export function _saveRecoveryRetries(n: number): void { try { sessionStorage.setItem(_RECOVERY_RETRIES_KEY, String(n)); } catch { /* non-blocking */ } } // Mutable retry counters exported as a container object to avoid ES module // binding assignment errors (live bindings of `let` exports can't be reassigned // by the importing module in strict ES module semantics). export const _retryCounts = { idleRetryLeft: (typeof window !== "undefined") ? _loadIdleRetries() : 2, recoveryRetryLeft: (typeof window !== "undefined") ? _loadRecoveryRetries() : 2, }; // Backward-compat shims: agentSSE.ts imports _idleRetryLeft and _recoveryRetryLeft // as named bindings and assigns to them. Re-export as getters on _retryCounts. // agentSSE.ts should use _retryCounts.idleRetryLeft directly for mutations. // These read-only re-exports satisfy the import statement without runtime errors. export const _idleRetryLeft = _retryCounts.idleRetryLeft; export const _recoveryRetryLeft = _retryCounts.recoveryRetryLeft;