openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
215 lines (214 loc) • 6.98 kB
JavaScript
//#region src/gateway/server-chat-state.ts
/** Create the FIFO registry that maps session IDs to active chat runs. */
function createChatRunRegistry() {
const chatRunSessions = /* @__PURE__ */ new Map();
const add = (sessionId, entry) => {
const queue = chatRunSessions.get(sessionId);
if (queue) queue.push(entry);
else chatRunSessions.set(sessionId, [entry]);
};
const peek = (sessionId) => chatRunSessions.get(sessionId)?.[0];
const shift = (sessionId) => {
const queue = chatRunSessions.get(sessionId);
if (!queue || queue.length === 0) return;
const entry = queue.shift();
if (!queue.length) chatRunSessions.delete(sessionId);
return entry;
};
const remove = (sessionId, clientRunId, sessionKey) => {
const queue = chatRunSessions.get(sessionId);
if (!queue || queue.length === 0) return;
const idx = queue.findIndex((entry) => entry.clientRunId === clientRunId && (sessionKey ? entry.sessionKey === sessionKey : true));
if (idx < 0) return;
const [entry] = queue.splice(idx, 1);
if (!queue.length) chatRunSessions.delete(sessionId);
return entry;
};
const clear = () => {
chatRunSessions.clear();
};
return {
add,
peek,
shift,
remove,
clear
};
}
/** Create all mutable chat-run maps used by Gateway runtime state. */
function createChatRunState() {
const registry = createChatRunRegistry();
const rawBuffers = /* @__PURE__ */ new Map();
const buffers = /* @__PURE__ */ new Map();
const bufferUpdatedAt = /* @__PURE__ */ new Map();
const deltaSentAt = /* @__PURE__ */ new Map();
const deltaLastBroadcastLen = /* @__PURE__ */ new Map();
const deltaLastBroadcastText = /* @__PURE__ */ new Map();
const agentDeltaSentAt = /* @__PURE__ */ new Map();
const bufferedAgentEvents = /* @__PURE__ */ new Map();
const abortedRuns = /* @__PURE__ */ new Map();
const clearRun = (runId) => {
rawBuffers.delete(runId);
buffers.delete(runId);
bufferUpdatedAt.delete(runId);
deltaSentAt.delete(runId);
deltaLastBroadcastLen.delete(runId);
deltaLastBroadcastText.delete(runId);
for (const key of [
runId,
`${runId}:assistant`,
`${runId}:thinking`
]) {
agentDeltaSentAt.delete(key);
bufferedAgentEvents.delete(key);
}
};
const clear = () => {
registry.clear();
rawBuffers.clear();
buffers.clear();
bufferUpdatedAt.clear();
deltaSentAt.clear();
deltaLastBroadcastLen.clear();
deltaLastBroadcastText.clear();
agentDeltaSentAt.clear();
bufferedAgentEvents.clear();
abortedRuns.clear();
};
return {
registry,
rawBuffers,
buffers,
bufferUpdatedAt,
deltaSentAt,
deltaLastBroadcastLen,
deltaLastBroadcastText,
agentDeltaSentAt,
bufferedAgentEvents,
abortedRuns,
clearRun,
clear
};
}
const TOOL_EVENT_RECIPIENT_TTL_MS = 600 * 1e3;
const TOOL_EVENT_RECIPIENT_FINAL_GRACE_MS = 30 * 1e3;
/** Create the broad sessions.changed subscriber registry. */
function createSessionEventSubscriberRegistry() {
const connIds = /* @__PURE__ */ new Set();
const empty = /* @__PURE__ */ new Set();
return {
subscribe: (connId) => {
const normalized = connId.trim();
if (!normalized) return;
connIds.add(normalized);
},
unsubscribe: (connId) => {
const normalized = connId.trim();
if (!normalized) return;
connIds.delete(normalized);
},
getAll: () => connIds.size > 0 ? connIds : empty,
clear: () => {
connIds.clear();
}
};
}
/** Create the per-session message subscriber registry. */
function createSessionMessageSubscriberRegistry() {
const sessionToConnIds = /* @__PURE__ */ new Map();
const connToSessionKeys = /* @__PURE__ */ new Map();
const empty = /* @__PURE__ */ new Set();
const normalize = (value) => value.trim();
return {
subscribe: (connId, sessionKey) => {
const normalizedConnId = normalize(connId);
const normalizedSessionKey = normalize(sessionKey);
if (!normalizedConnId || !normalizedSessionKey) return;
const connIds = sessionToConnIds.get(normalizedSessionKey) ?? /* @__PURE__ */ new Set();
connIds.add(normalizedConnId);
sessionToConnIds.set(normalizedSessionKey, connIds);
const sessionKeys = connToSessionKeys.get(normalizedConnId) ?? /* @__PURE__ */ new Set();
sessionKeys.add(normalizedSessionKey);
connToSessionKeys.set(normalizedConnId, sessionKeys);
},
unsubscribe: (connId, sessionKey) => {
const normalizedConnId = normalize(connId);
const normalizedSessionKey = normalize(sessionKey);
if (!normalizedConnId || !normalizedSessionKey) return;
const connIds = sessionToConnIds.get(normalizedSessionKey);
if (connIds) {
connIds.delete(normalizedConnId);
if (connIds.size === 0) sessionToConnIds.delete(normalizedSessionKey);
}
const sessionKeys = connToSessionKeys.get(normalizedConnId);
if (sessionKeys) {
sessionKeys.delete(normalizedSessionKey);
if (sessionKeys.size === 0) connToSessionKeys.delete(normalizedConnId);
}
},
unsubscribeAll: (connId) => {
const normalizedConnId = normalize(connId);
if (!normalizedConnId) return;
const sessionKeys = connToSessionKeys.get(normalizedConnId);
if (!sessionKeys) return;
for (const sessionKey of sessionKeys) {
const connIds = sessionToConnIds.get(sessionKey);
if (!connIds) continue;
connIds.delete(normalizedConnId);
if (connIds.size === 0) sessionToConnIds.delete(sessionKey);
}
connToSessionKeys.delete(normalizedConnId);
},
get: (sessionKey) => {
const normalizedSessionKey = normalize(sessionKey);
if (!normalizedSessionKey) return empty;
return sessionToConnIds.get(normalizedSessionKey) ?? empty;
},
clear: () => {
sessionToConnIds.clear();
connToSessionKeys.clear();
}
};
}
/** Create the run-id recipient registry used for streaming tool events. */
function createToolEventRecipientRegistry() {
const recipients = /* @__PURE__ */ new Map();
const prune = () => {
if (recipients.size === 0) return;
const now = Date.now();
for (const [runId, entry] of recipients) if (now >= (entry.finalizedAt ? entry.finalizedAt + TOOL_EVENT_RECIPIENT_FINAL_GRACE_MS : entry.updatedAt + TOOL_EVENT_RECIPIENT_TTL_MS)) recipients.delete(runId);
};
const add = (runId, connId) => {
if (!runId || !connId) return;
const now = Date.now();
const existing = recipients.get(runId);
if (existing) {
existing.connIds.add(connId);
existing.updatedAt = now;
} else recipients.set(runId, {
connIds: new Set([connId]),
updatedAt: now
});
prune();
};
const get = (runId) => {
const entry = recipients.get(runId);
if (!entry) return;
entry.updatedAt = Date.now();
prune();
return entry.connIds;
};
const markFinal = (runId) => {
const entry = recipients.get(runId);
if (!entry) return;
entry.finalizedAt = Date.now();
prune();
};
return {
add,
get,
markFinal
};
}
//#endregion
export { createToolEventRecipientRegistry as a, createSessionMessageSubscriberRegistry as i, createChatRunState as n, createSessionEventSubscriberRegistry as r, createChatRunRegistry as t };