UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

114 lines (113 loc) 4.71 kB
import { i as resolveGlobalSingleton } from "./global-singleton-Dc_stLtU.js"; import { t as pruneMapToMaxSize } from "./map-size-CNcWiFKu.js"; import { t as listSystemPresence } from "./system-presence-Y_GcutUC.js"; import { t as presenceUserKey } from "./presence-user-ulXqGuvR.js"; //#region src/gateway/server-methods/session-typing-state.ts const TYPING_THROTTLE_MS = 1e3; const TYPING_ACTIVE_TTL_MS = 2500; const MAX_TYPING_THROTTLE_KEYS = 2048; function clearSessionTypingStateValue(state) { for (const entry of state.broadcasts.values()) if (entry.timer) clearTimeout(entry.timer); state.broadcasts.clear(); state.connections.clear(); } const sessionTypingState = resolveGlobalSingleton(Symbol.for("openclaw.sessionTypingState"), () => ({ broadcasts: /* @__PURE__ */ new Map(), connections: /* @__PURE__ */ new Map() }), clearSessionTypingStateValue); const typingBroadcastState = sessionTypingState.broadcasts; const typingConnections = sessionTypingState.connections; function clearSessionTypingState() { clearSessionTypingStateValue(sessionTypingState); } function liveViewerIdentities(sessionKeys) { return new Set(listSystemPresence().flatMap((entry) => entry.user?.id && entry.watchedSessions?.some((sessionKey) => sessionKeys.has(sessionKey)) ? [presenceUserKey(entry.user)] : [])); } function rememberTypingBroadcast(key, state) { typingBroadcastState.delete(key); typingBroadcastState.set(key, state); if (typingBroadcastState.size <= MAX_TYPING_THROTTLE_KEYS) return; const oldestKey = typingBroadcastState.keys().next().value; if (!oldestKey) return; const oldest = typingBroadcastState.get(oldestKey); if (oldest?.timer) clearTimeout(oldest.timer); typingBroadcastState.delete(oldestKey); } function broadcastTypingThrottled(params) { const previous = typingBroadcastState.get(params.key); if (!previous || params.now - previous.at >= params.intervalMs) { if (previous?.timer) clearTimeout(previous.timer); const emitted = params.emit(); if (emitted) rememberTypingBroadcast(params.key, { at: params.now, typing: params.typing, signature: params.signature }); else typingBroadcastState.delete(params.key); return emitted; } if (params.signature === previous.signature && previous.pending?.signature !== params.signature) { if (previous.timer) clearTimeout(previous.timer); delete previous.pending; delete previous.timer; if (!params.typing) { rememberTypingBroadcast(params.key, previous); return false; } } if (previous.timer && previous.pending?.intervalMs !== params.intervalMs) { clearTimeout(previous.timer); delete previous.timer; } previous.pending = { typing: params.typing, signature: params.signature, intervalMs: params.intervalMs, emit: params.emit }; if (!previous.timer) { const timer = setTimeout(() => { const current = typingBroadcastState.get(params.key); if (!current || current.timer !== timer || !current.pending) return; const pending = current.pending; const next = { at: Date.now(), typing: pending.typing, signature: pending.signature }; if (pending.emit()) rememberTypingBroadcast(params.key, next); else typingBroadcastState.delete(params.key); }, params.intervalMs - (params.now - previous.at)); timer.unref?.(); previous.timer = timer; } rememberTypingBroadcast(params.key, previous); return false; } function updateTypingConnections(params) { for (const [typingKey, activeConnections] of typingConnections) { for (const [connectionId, connection] of activeConnections) if (params.now - connection.updatedAt >= TYPING_ACTIVE_TTL_MS) activeConnections.delete(connectionId); if (activeConnections.size === 0) typingConnections.delete(typingKey); } const connections = typingConnections.get(params.key) ?? /* @__PURE__ */ new Map(); if (params.typing) connections.set(params.connectionId, { updatedAt: params.now, ...params.preview ? { preview: params.preview } : {} }); else connections.delete(params.connectionId); if (connections.size === 0) { typingConnections.delete(params.key); return { typing: false }; } typingConnections.delete(params.key); typingConnections.set(params.key, connections); pruneMapToMaxSize(typingConnections, MAX_TYPING_THROTTLE_KEYS); let latestPreview; for (const connection of connections.values()) if (connection.preview && (!latestPreview || connection.updatedAt >= latestPreview.updatedAt)) latestPreview = connection; return { typing: true, ...latestPreview?.preview ? { preview: latestPreview.preview } : {} }; } //#endregion export { updateTypingConnections as a, liveViewerIdentities as i, broadcastTypingThrottled as n, clearSessionTypingState as r, TYPING_THROTTLE_MS as t };