Files
openclaw/packages/acp-core/dist/session.mjs
Peter Steinberger 7dea283756 refactor: expand acp core package (#88618)
* refactor: expand acp core package

* chore: drop acp core package symlink

* fix: keep acp core dependency graph stable

* fix: add acp core tsconfig subpaths

* fix: sync acp core boundary path artifacts

* fix: use kysely for cron run-log queries

* fix: resolve acp core subpaths in loaders
2026-05-31 14:48:57 +01:00

129 lines
4.5 KiB
JavaScript

import { resolveIntegerOption } from "./numeric-options.mjs";
import { randomUUID } from "node:crypto";
//#region src/session.ts
const DEFAULT_MAX_SESSIONS = 5e3;
const DEFAULT_IDLE_TTL_MS = 1440 * 60 * 1e3;
function createInMemorySessionStore(options = {}) {
const maxSessions = resolveIntegerOption(options.maxSessions, DEFAULT_MAX_SESSIONS, { min: 1 });
const idleTtlMs = resolveIntegerOption(options.idleTtlMs, DEFAULT_IDLE_TTL_MS, { min: 1e3 });
const now = options.now ?? Date.now;
const sessions = /* @__PURE__ */ new Map();
const runIdToSessionId = /* @__PURE__ */ new Map();
const touchSession = (session, nowMs) => {
session.lastTouchedAt = nowMs;
};
const removeSession = (sessionId) => {
const session = sessions.get(sessionId);
if (!session) return false;
if (session.activeRunId) runIdToSessionId.delete(session.activeRunId);
session.abortController?.abort();
sessions.delete(sessionId);
return true;
};
const reapIdleSessions = (nowMs) => {
const idleBefore = nowMs - idleTtlMs;
for (const [sessionId, session] of sessions.entries()) {
if (session.activeRunId || session.abortController) continue;
if (session.lastTouchedAt > idleBefore) continue;
removeSession(sessionId);
}
};
const evictOldestIdleSession = () => {
let oldestSessionId = null;
let oldestLastTouchedAt = Number.POSITIVE_INFINITY;
for (const [sessionId, session] of sessions.entries()) {
if (session.activeRunId || session.abortController) continue;
if (session.lastTouchedAt >= oldestLastTouchedAt) continue;
oldestLastTouchedAt = session.lastTouchedAt;
oldestSessionId = sessionId;
}
if (!oldestSessionId) return false;
return removeSession(oldestSessionId);
};
const createSession = (params) => {
const nowMs = now();
const sessionId = params.sessionId ?? randomUUID();
const existingSession = sessions.get(sessionId);
if (existingSession) {
existingSession.sessionKey = params.sessionKey;
if ("ledgerSessionId" in params) existingSession.ledgerSessionId = params.ledgerSessionId;
existingSession.cwd = params.cwd;
touchSession(existingSession, nowMs);
return existingSession;
}
reapIdleSessions(nowMs);
if (sessions.size >= maxSessions && !evictOldestIdleSession()) throw new Error(`ACP session limit reached (max ${maxSessions}). Close idle ACP clients and retry.`);
const session = {
sessionId,
sessionKey: params.sessionKey,
...params.ledgerSessionId ? { ledgerSessionId: params.ledgerSessionId } : {},
cwd: params.cwd,
createdAt: nowMs,
lastTouchedAt: nowMs,
abortController: null,
activeRunId: null
};
sessions.set(sessionId, session);
return session;
};
const hasSession = (sessionId) => sessions.has(sessionId);
const getSession = (sessionId) => {
const session = sessions.get(sessionId);
if (session) touchSession(session, now());
return session;
};
const getSessionByRunId = (runId) => {
const sessionId = runIdToSessionId.get(runId);
if (!sessionId) return;
const session = sessions.get(sessionId);
if (session) touchSession(session, now());
return session;
};
const setActiveRun = (sessionId, runId, abortController) => {
const session = sessions.get(sessionId);
if (!session) return;
session.activeRunId = runId;
session.abortController = abortController;
runIdToSessionId.set(runId, sessionId);
touchSession(session, now());
};
const clearActiveRun = (sessionId) => {
const session = sessions.get(sessionId);
if (!session) return;
if (session.activeRunId) runIdToSessionId.delete(session.activeRunId);
session.activeRunId = null;
session.abortController = null;
touchSession(session, now());
};
const cancelActiveRun = (sessionId) => {
const session = sessions.get(sessionId);
if (!session?.abortController) return false;
session.abortController.abort();
if (session.activeRunId) runIdToSessionId.delete(session.activeRunId);
session.abortController = null;
session.activeRunId = null;
touchSession(session, now());
return true;
};
const deleteSession = (sessionId) => removeSession(sessionId);
const clearAllSessionsForTest = () => {
for (const session of sessions.values()) session.abortController?.abort();
sessions.clear();
runIdToSessionId.clear();
};
return {
createSession,
hasSession,
getSession,
getSessionByRunId,
setActiveRun,
clearActiveRun,
cancelActiveRun,
deleteSession,
clearAllSessionsForTest
};
}
const defaultAcpSessionStore = createInMemorySessionStore();
//#endregion
export { createInMemorySessionStore, defaultAcpSessionStore };