openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
766 lines (765 loc) • 34.6 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { n as normalizeAgentId, r as normalizeAgentIdStrict } from "./agent-id-CeT3w4ap.js";
import { o as listAgentIds } from "./agent-scope-config-DcbEhP0R.js";
import { t as pruneMapToMaxSize } from "./map-size-CNcWiFKu.js";
import { O as parseAgentSessionKey } from "./session-key-BnWWjqNc.js";
import { t as isIncognitoSessionKey } from "./incognito-session-key-BwpD1Lwd.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { o as measureDiagnosticsTimelineSpan, s as measureDiagnosticsTimelineSpanSync } from "./diagnostics-timeline-CRX1LXmg.js";
import { o as resolveSessionStorePathCore } from "./paths-CXdaYWF_.js";
import { n as resolvePersistedSessionStoreOwnerForKey } from "./session-store-owner-CR2Ag3xK.js";
import { t as ErrorCodes } from "./gateway-error-details-w0nAGBBp.js";
import { p as readAgentRunIndexVersion } from "./agent-run-registry-CKYKdfNd.js";
import { Ii as validateSessionsSearchParams, Kr as validateSessionsCleanupParams, Pi as validateSessionsResolveParams, ki as validateSessionsPreviewParams, ni as validateSessionsDescribeParams, xi as validateSessionsListParams } from "./src-BiL5aQto.js";
import { d as errorShape } from "./error-codes-Bo8q2D1o.js";
import { b as readOpenClawAgentDatabaseRegistryToken } from "./openclaw-agent-db-maintenance-wTIy-jt-.js";
import { f as listOpenIncognitoAgentDatabases, m as readOpenIncognitoAgentDatabaseGeneration } from "./openclaw-agent-db-CWtDoRbC.js";
import "./session-accessor-YsytfDtG.js";
import { a as listSessionEntriesReadOnly } from "./session-accessor.sqlite-entry-CWk3jL7s.js";
import { r as resolveSqliteTargetFromSessionStorePath } from "./session-sqlite-target-Dp-kgpcT.js";
import { g as readSessionLifecycleVersion, h as readSessionIdentityMutationVersion } from "./session-history-eviction-C4srftLJ.js";
import { o as readSessionTranscriptUpdateVersion } from "./transcript-events-wgPr4ILk.js";
import { a as resolveStoredSessionKeyForAgentStore, i as resolveSessionStoreKey, n as resolveSessionStoreAgentId } from "./session-store-key-8xEjWSNi.js";
import { l as resolveExistingAgentSessionStoreTargetsSync, t as isConfiguredSessionStoreAgentId } from "./targets-Cknmo7YZ.js";
import { s as readUserProfileVersion } from "./user-profiles-owner-DdMEsuGt.js";
import { n as loadCombinedSessionStoreForGatewayCore } from "./combined-store-gateway-DGm4nZAJ.js";
import { u as listSessionMembershipKeys } from "./sessions-9nxpeTwt.js";
import { n as runSessionsCleanup, o as serializeSessionCleanupResult } from "./cleanup-service-DoQBUGSQ.js";
import { h as gatewayClientSessionCreator, i as operatorSessionCap, n as hasOperatorBoundary } from "./operator-role-policy-wsr1DeJv.js";
import { n as readRecentSessionMessagesWithStatsAsync, u as readSessionPreviewItemsFromTranscript } from "./session-transcript-readers-CYDRQsH5.js";
import { f as readSessionTitleProjectionUnavailableVersion, l as projectGatewaySessionActiveRun, n as listSessionsFromStoreAsync, r as resolveSessionsListDefaultsAgentId, s as buildGatewaySessionRow, y as readSessionAutomationVersion } from "./session-utils-list-B0k8KJn5.js";
import { i as tryResolveSessionCompatibilityOwnerAgentId, n as resolveRequestedSessionAgentId } from "./session-request-agent-CCRSEGCB.js";
import { f as resolveGatewaySessionStoreTargetWithStore, s as resolveCanonicalSessionEntryFromStoreKeys, u as createGatewaySessionStoreDiscoveryCache } from "./session-utils-store-CInT2loy.js";
import "./session-utils-Cai0_C6U.js";
import { B as resolveSessionSharingTargets, F as isGatewayAdmin, N as canAccessIncognitoSession, V as resolveSessionVisibility, i as prepareSessionSharing, r as createSessionListEntryFilter, z as resolveSessionSharingTarget } from "./session-sharing-B7MI8hNo.js";
import { t as searchSessionTranscripts } from "./session-transcript-search-BMctzdWO.js";
import { o as readSessionLifecyclePersistenceVersion } from "./session-lifecycle-state-DCd4j8Bd.js";
import { t as resolveSessionKeyFromResolveParams } from "./sessions-resolve-CjCEkUZ8.js";
import { t as assertValidParams } from "./validation-pzrlzFvo.js";
import { t as createVisibleActiveSessionRunProjector } from "./session-active-runs-CCNuJW7B.js";
import { t as resolveGatewayModelSelectionPolicy } from "./session-model-selection-policy-BiBt__vw.js";
import { n as readSessionPlacementFields, t as createSessionPlacementBatchProjector } from "./session-placement-read-projection-eZkSl0va.js";
import { i as readSessionsMutationVersion, n as emitSessionsChanged } from "./session-change-event-DzmH4zlz.js";
import { n as listBoardSessionKeysReadOnly } from "./sqlite-board-store-B92YW4eF.js";
import { u as readSessionObserverDigestVersion } from "./session-observer-model-D5MYTAfa.js";
import { t as readPreparedServerMethodModelCatalog } from "./optional-model-catalog-CIJvOLOk.js";
import { a as loadSessionEntriesForTarget, s as requireSessionKey } from "./sessions-shared-D_lV07q2.js";
import { setImmediate } from "node:timers/promises";
//#region src/gateway/server-methods/sessions-board-inventory.ts
function listBoardSessionKeysByAgent(targets, requestedAgentId) {
const normalizedRequestedAgentId = requestedAgentId ? normalizeAgentId(requestedAgentId) : void 0;
const byAgent = /* @__PURE__ */ new Map();
for (const target of targets) {
if (normalizedRequestedAgentId && target.agentId !== normalizedRequestedAgentId) continue;
const databaseTarget = resolveSqliteTargetFromSessionStorePath(target.storePath, { agentId: target.agentId });
const databaseAgentId = normalizeAgentId(databaseTarget.agentId ?? target.agentId);
for (const sessionKey of listBoardSessionKeysReadOnly({
agentId: databaseAgentId,
path: databaseTarget.path
})) {
const agentId = normalizeAgentId(parseAgentSessionKey(sessionKey)?.agentId ?? target.agentId);
const keys = byAgent.get(agentId) ?? /* @__PURE__ */ new Set();
keys.add(sessionKey);
byAgent.set(agentId, keys);
}
}
return byAgent;
}
function listFilter(input) {
const { loaded, p: params } = input;
const visibilityFilter = prepareSessionSharing({
client: input.client,
cfg: input.cfg
}).entryFilter;
const excludedKeys = input.options.excludedKeys;
const boardSessionKeys = params.hasBoard === void 0 ? void 0 : {
durable: listBoardSessionKeysByAgent(loaded.durableTargets, params.agentId),
incognito: listBoardSessionKeysByAgent(listOpenIncognitoAgentDatabases(), params.agentId)
};
if (!visibilityFilter && !boardSessionKeys && !excludedKeys?.size) return;
return (key, entry) => {
const agentId = normalizeAgentId(loaded.agentIdBySessionKey.get(key) ?? parseAgentSessionKey(key)?.agentId ?? input.defaultsAgentId);
const hasBoard = (entry.incognito === true ? boardSessionKeys?.incognito : boardSessionKeys?.durable)?.get(agentId)?.has(key) ?? false;
return !excludedKeys?.has(key) && (visibilityFilter?.(key, entry) ?? true) && (params.hasBoard === void 0 || hasBoard === params.hasBoard);
};
}
//#endregion
//#region src/gateway/server-methods/sessions-list-cache.ts
const SESSIONS_LIST_COMPLETED_CACHE_LIMIT = 64;
const sessionListsByContext = /* @__PURE__ */ new WeakMap();
const modelCatalogRevisions = /* @__PURE__ */ new WeakMap();
let nextModelCatalogRevision = 1;
function readModelCatalogRevision(modelCatalog) {
if (!modelCatalog) return 0;
const existing = modelCatalogRevisions.get(modelCatalog);
if (existing !== void 0) return existing;
const revision = nextModelCatalogRevision++;
modelCatalogRevisions.set(modelCatalog, revision);
return revision;
}
/**
* Serializes the per-agent catalog revision set so the cache fence advances
* when any row owner's entries or provider policy changes. A new read wrapper
* alone does not invalidate the cache; the prepared facts retain stable identity.
*/
function readSessionListModelCatalogFence(modelCatalog) {
if (!modelCatalog || modelCatalog.size === 0) return "none";
return [...modelCatalog.entries()].toSorted(([left], [right]) => left.localeCompare(right)).map(([agentId, catalog]) => `${agentId}:${readModelCatalogRevision(catalog?.entries)}:${readModelCatalogRevision(catalog?.pluginRegistry)}`).join(",");
}
function readSessionListFence(context) {
return {
agentRunIndexVersion: readAgentRunIndexVersion(),
agentDatabaseRegistryToken: readOpenClawAgentDatabaseRegistryToken(),
incognitoDatabaseGeneration: readOpenIncognitoAgentDatabaseGeneration(),
lifecyclePersistenceVersion: readSessionLifecyclePersistenceVersion(),
sessionAutomationVersion: readSessionAutomationVersion(),
sessionIdentityMutationVersion: readSessionIdentityMutationVersion(),
sessionLifecycleVersion: readSessionLifecycleVersion(),
sessionObserverDigestVersion: readSessionObserverDigestVersion(),
userProfileVersion: readUserProfileVersion(),
sessionsMutationVersion: readSessionsMutationVersion(context),
sessionTranscriptUpdateVersion: readSessionTranscriptUpdateVersion(),
titleProjectionUnavailableVersion: readSessionTitleProjectionUnavailableVersion(),
workerEnvironmentInventoryVersion: context.workerEnvironmentService?.inventoryVersion() ?? 0,
workerPlacementDiskSpaceVersion: context.workerPlacementDiskSpaceReader?.version() ?? 0,
workerPlacementRunnerAvailabilityVersion: context.workerPlacementRunnerAvailabilityReader?.version() ?? 0
};
}
function matchesSessionListFence(value, fence) {
return value.agentRunIndexVersion === fence.agentRunIndexVersion && value.agentDatabaseRegistryToken === fence.agentDatabaseRegistryToken && value.incognitoDatabaseGeneration === fence.incognitoDatabaseGeneration && value.lifecyclePersistenceVersion === fence.lifecyclePersistenceVersion && value.sessionAutomationVersion === fence.sessionAutomationVersion && value.sessionIdentityMutationVersion === fence.sessionIdentityMutationVersion && value.sessionLifecycleVersion === fence.sessionLifecycleVersion && value.sessionObserverDigestVersion === fence.sessionObserverDigestVersion && value.userProfileVersion === fence.userProfileVersion && value.sessionsMutationVersion === fence.sessionsMutationVersion && value.sessionTranscriptUpdateVersion === fence.sessionTranscriptUpdateVersion && value.titleProjectionUnavailableVersion === fence.titleProjectionUnavailableVersion && value.workerEnvironmentInventoryVersion === fence.workerEnvironmentInventoryVersion && value.workerPlacementDiskSpaceVersion === fence.workerPlacementDiskSpaceVersion && value.workerPlacementRunnerAvailabilityVersion === fence.workerPlacementRunnerAvailabilityVersion;
}
function sessionListWorkKey(params, client, config) {
return JSON.stringify([
gatewayClientSessionCreator(client)?.id ?? null,
isGatewayAdmin(client) ? "admin" : operatorSessionCap(client, config) ?? null,
Object.entries(params).toSorted(([left], [right]) => left.localeCompare(right))
]);
}
function sessionListState(context, config) {
let state = sessionListsByContext.get(context);
const fence = readSessionListFence(context);
if (!state || state.config !== config || !matchesSessionListFence(state, fence)) {
state?.completed.clear();
state = {
...fence,
completed: /* @__PURE__ */ new Map(),
config,
inFlight: /* @__PURE__ */ new Map()
};
sessionListsByContext.set(context, state);
}
return state;
}
function readCompletedSessionList(state, workKey, modelCatalogRevision) {
const completed = state.completed.get(workKey);
if (completed?.modelCatalogRevision === modelCatalogRevision && (completed.expiresAt === void 0 || completed.expiresAt > Date.now())) return completed.result;
state.completed.delete(workKey);
}
function resolveSessionListExpiration(result) {
let expiresAt;
for (const session of result.sessions) {
if (session.hasActiveRun || session.hasActiveSubagentRun || session.childSessions?.length) return null;
const statusExpiration = session.agentStatus?.expiresAt;
if (statusExpiration !== void 0 && (expiresAt === void 0 || statusExpiration < expiresAt)) expiresAt = statusExpiration;
}
return expiresAt;
}
async function respondWithCachedSessionList(params) {
const workKey = sessionListWorkKey(params.request, params.client, params.config);
const state = sessionListState(params.context, params.config);
const modelCatalogRevision = readSessionListModelCatalogFence(params.modelCatalog);
const cacheCompleted = params.request.activeMinutes === void 0 && !params.request.spawnedBy && !params.request.search?.trim();
const completed = cacheCompleted ? readCompletedSessionList(state, workKey, modelCatalogRevision) : void 0;
if (completed) {
params.respond(true, completed, void 0);
return;
}
const pending = state.inFlight.get(workKey);
if (pending?.modelCatalogRevision === modelCatalogRevision) {
params.respond(true, await pending.promise, void 0);
return;
}
const promise = Promise.resolve().then(params.run).then((result) => {
if (cacheCompleted && sessionListsByContext.get(params.context) === state && matchesSessionListFence(state, readSessionListFence(params.context)) && readSessionListModelCatalogFence(params.modelCatalog) === modelCatalogRevision) {
const expiresAt = resolveSessionListExpiration(result);
if (expiresAt !== null && (expiresAt === void 0 || expiresAt > Date.now())) {
state.completed.delete(workKey);
state.completed.set(workKey, {
modelCatalogRevision,
result,
expiresAt
});
pruneMapToMaxSize(state.completed, SESSIONS_LIST_COMPLETED_CACHE_LIMIT);
}
}
return result;
});
const operation = {
modelCatalogRevision,
promise
};
state.inFlight.set(workKey, operation);
try {
params.respond(true, await promise, void 0);
} finally {
if (state.inFlight.get(workKey) === operation) state.inFlight.delete(workKey);
}
}
//#endregion
//#region src/gateway/server-methods/sessions-read-by-key.ts
function createRoleVisibilityFilter(client, cfg) {
return hasOperatorBoundary(client, cfg) ? createSessionListEntryFilter({
client,
cfg
}) : void 0;
}
const sessionByKeyReadHandlers = {
"sessions.describe": ({ params, respond, context, client }) => {
if (!assertValidParams(params, validateSessionsDescribeParams, "sessions.describe", respond)) return;
const key = requireSessionKey(params.key, respond);
if (!key) return;
const cfg = context.getRuntimeConfig();
const requestedAgent = resolveRequestedSessionAgentId(cfg, key);
if (!requestedAgent.ok) {
respond(false, void 0, requestedAgent.error);
return;
}
const { target, storePath, store, entry } = loadSessionEntriesForTarget({
key,
cfg,
...requestedAgent.agentId ? { agentId: requestedAgent.agentId } : {}
});
const boundaryFilter = createRoleVisibilityFilter(client, cfg);
if (!entry || boundaryFilter?.(target.canonicalKey, entry) === false) {
respond(true, { session: null }, void 0);
return;
}
const row = buildGatewaySessionRow({
cfg,
storePath,
store,
key: target.canonicalKey,
entry,
agentId: target.agentId,
includeDerivedTitles: params.includeDerivedTitles,
includeLastMessage: params.includeLastMessage,
transcriptUsageMaxBytes: 65536
});
Object.assign(row, readSessionPlacementFields(context, row.sessionId));
respond(true, { session: row });
},
"sessions.get": async ({ params, respond, context, client }) => {
const p = params;
const key = requireSessionKey(p.key ?? p.sessionKey, respond);
if (!key) return;
const limit = typeof p.limit === "number" && Number.isFinite(p.limit) ? Math.max(1, Math.floor(p.limit)) : 200;
const cfg = context.getRuntimeConfig();
const requestedAgent = resolveRequestedSessionAgentId(cfg, key, normalizeOptionalString(p.agentId));
if (!requestedAgent.ok) {
respond(false, void 0, requestedAgent.error);
return;
}
const { target, storePath, entry } = loadSessionEntriesForTarget({
key,
cfg,
agentId: requestedAgent.agentId
});
const boundaryFilter = createRoleVisibilityFilter(client, cfg);
if (!entry?.sessionId || boundaryFilter?.(target.canonicalKey, entry) === false) {
respond(true, { messages: [] }, void 0);
return;
}
const sessionId = entry.sessionId;
const { messages } = await readRecentSessionMessagesWithStatsAsync({
agentId: target.agentId,
sessionEntry: entry,
sessionId,
sessionKey: target.canonicalKey,
storePath
}, {
maxMessages: limit,
maxLines: limit * 20 + 20,
allowResetArchiveFallback: true
});
const currentCfg = context.getRuntimeConfig();
const currentRequestedAgent = resolveRequestedSessionAgentId(currentCfg, key, normalizeOptionalString(p.agentId));
const current = currentRequestedAgent.ok ? loadSessionEntriesForTarget({
key,
cfg: currentCfg,
agentId: currentRequestedAgent.agentId
}) : null;
const currentBoundaryFilter = createRoleVisibilityFilter(client, currentCfg);
if (!current || current.target.agentId !== target.agentId || current.target.canonicalKey !== target.canonicalKey || current.storePath !== storePath || current.entry?.sessionId !== sessionId || currentBoundaryFilter?.(current.target.canonicalKey, current.entry) === false) {
respond(true, { messages: [] }, void 0);
return;
}
respond(true, { messages }, void 0);
}
};
//#endregion
//#region src/gateway/server-methods/sessions-search-scope.ts
function resolveSessionSearchScope(cfg, params) {
const normalizedRequest = params.agentId === void 0 ? null : normalizeAgentIdStrict(params.agentId);
if (normalizedRequest && !normalizedRequest.ok) return {
ok: false,
error: errorShape(ErrorCodes.INVALID_REQUEST, `Unknown agent id "${params.agentId}"`)
};
const requestedAgentId = normalizedRequest?.value;
const resolvedSessionKeys = params.sessionKeys ? [] : void 0;
for (const sessionKey of params.sessionKeys ?? []) {
const requestedAgent = requestedAgentId && !isConfiguredSessionStoreAgentId(cfg, requestedAgentId) && resolvePersistedSessionStoreOwnerForKey(cfg, sessionKey).kind === "none" ? {
ok: true,
agentId: requestedAgentId
} : resolveRequestedSessionAgentId(cfg, sessionKey, requestedAgentId);
if (!requestedAgent.ok) return {
ok: false,
error: requestedAgent.error
};
resolvedSessionKeys?.push({
sessionKey: requestedAgent.agentId ? resolveStoredSessionKeyForAgentStore({
cfg,
agentId: requestedAgent.agentId,
sessionKey
}) : resolveSessionStoreKey({
cfg,
sessionKey
}),
agentId: requestedAgent.agentId
});
}
const sessionKeys = resolvedSessionKeys?.map((resolved) => resolved.sessionKey);
const agentIds = new Set(resolvedSessionKeys?.map((resolved) => resolved.agentId ? resolved.agentId : resolveSessionStoreAgentId(cfg, resolved.sessionKey)));
if (agentIds.size > 1 || requestedAgentId && [...agentIds].some((agentId) => agentId !== requestedAgentId)) return {
ok: false,
error: errorShape(ErrorCodes.INVALID_REQUEST, "sessions.search supports one agent per call")
};
let agentId = requestedAgentId ?? agentIds.values().next().value;
if (!agentId) {
const fallbackAgent = resolveRequestedSessionAgentId(cfg, "main");
if (!fallbackAgent.ok) return {
ok: false,
error: fallbackAgent.error
};
agentId = fallbackAgent.agentId;
}
return {
ok: true,
agentId,
configured: isConfiguredSessionStoreAgentId(cfg, agentId),
requestedAgentId,
sessionKeys
};
}
//#endregion
//#region src/gateway/server-methods/sessions-read.ts
const sessionReadHandlers = {
"sessions.search": async ({ params, respond, context, client }) => {
if (!assertValidParams(params, validateSessionsSearchParams, "sessions.search", respond)) return;
const query = params.query.trim();
if (!query) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "query must not be empty"));
return;
}
const cfg = context.getRuntimeConfig();
const scope = resolveSessionSearchScope(cfg, params);
if (!scope.ok) {
respond(false, void 0, scope.error);
return;
}
const { agentId, configured, requestedAgentId, sessionKeys } = scope;
const restrictIncognito = Boolean(gatewayClientSessionCreator(client)) && !isGatewayAdmin(client);
const roleVisibilityFilter = hasOperatorBoundary(client, cfg) ? createSessionListEntryFilter({
client,
cfg
}) : void 0;
const restrictVisibility = restrictIncognito || Boolean(roleVisibilityFilter);
const canSearchSessionKey = (sessionKey) => {
if (isIncognitoSessionKey(sessionKey) && !canAccessIncognitoSession({
cfg,
client: client ?? null,
sessionKey,
agentId
})) return false;
if (!roleVisibilityFilter) return true;
const target = resolveSessionSharingTarget({
cfg,
sessionKey,
agentId
});
return Boolean(target && roleVisibilityFilter(target.storeKey, target.entry));
};
if (requestedAgentId && !params.sessionKeys && configured) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "agentId requires sessionKeys"));
return;
}
const scopedSessionKeys = (configured ? sessionKeys : sessionKeys?.filter((sessionKey) => {
return (requestedAgentId && (sessionKey === "global" || sessionKey === "unknown") ? requestedAgentId : resolveSessionStoreAgentId(cfg, sessionKey)) === agentId;
}))?.filter(canSearchSessionKey);
const searchTargets = configured ? [{
agentId,
storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId })
}] : resolveExistingAgentSessionStoreTargetsSync(cfg, agentId);
if (!configured && (searchTargets.length === 0 || scopedSessionKeys?.length === 0)) {
respond(true, { results: [] }, void 0);
return;
}
try {
const targetResults = searchTargets.flatMap((target) => {
const targetSessionKeys = scopedSessionKeys ?? (restrictVisibility ? listSessionEntriesReadOnly({
agentId: target.agentId,
storePath: target.storePath
}).map((entry) => entry.sessionKey).filter((sessionKey) => {
if (!canSearchSessionKey(sessionKey)) return false;
const parsed = parseAgentSessionKey(sessionKey);
return !parsed || normalizeAgentId(parsed.agentId) === agentId;
}) : void 0);
if (targetSessionKeys?.length === 0) return [];
return [searchSessionTranscripts({
...target,
query,
limit: configured ? params.limit : 25,
...targetSessionKeys ? { sessionKeys: targetSessionKeys } : {}
})];
});
const limit = params.limit ?? 10;
const sortedHits = targetResults.flatMap((result) => result.hits).toSorted((left, right) => right.score - left.score || right.timestamp - left.timestamp || left.messageId.localeCompare(right.messageId));
const seenHits = /* @__PURE__ */ new Set();
const hits = sortedHits.filter((hit) => {
const identity = `${hit.sessionKey}\u0000${hit.sessionId}\u0000${hit.messageId}`;
if (seenHits.has(identity)) return false;
seenHits.add(identity);
return true;
});
respond(true, {
results: hits.slice(0, limit),
...targetResults.some((result) => result.indexing) ? { indexing: true } : {},
...targetResults.some((result) => result.truncated) || hits.length > limit ? { truncated: true } : {}
});
} catch (error) {
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, formatErrorMessage(error)));
}
},
"sessions.list": async ({ params, respond, client, context }) => {
if (!assertValidParams(params, validateSessionsListParams, "sessions.list", respond)) return;
const p = params;
const cfg = context.getRuntimeConfig();
const configuredAgentsOnly = p.configuredAgentsOnly === true;
const identityId = gatewayClientSessionCreator(client)?.id;
const defaultsAgentId = resolveSessionsListDefaultsAgentId(cfg, p.agentId);
const modelSelectionTarget = resolveGatewayModelSelectionPolicy({
agentId: defaultsAgentId,
callerScopes: client?.connect?.scopes ?? [],
cfg
}).target;
const preparedModelCatalogByAgent = await measureDiagnosticsTimelineSpan("gateway.sessions.list.model_catalog", async () => {
const catalogByAgent = /* @__PURE__ */ new Map();
const agentIds = p.agentId ? [normalizeAgentId(p.agentId)] : listAgentIds(cfg);
for (const agentId of agentIds) catalogByAgent.set(agentId, await readPreparedServerMethodModelCatalog(context, { agentId }));
return catalogByAgent;
}, {
config: cfg,
phase: "sessions.list"
});
const run = () => measureDiagnosticsTimelineSpan("gateway.sessions.list", async function listVisibleSessions(options = {}) {
let loaded = options.loaded;
if (!loaded) loaded = {
...measureDiagnosticsTimelineSpanSync("gateway.sessions.list.store_load", () => loadCombinedSessionStoreForGatewayCore(cfg, {
agentId: p.agentId,
configuredAgentsOnly,
projection: "list"
}), {
config: cfg,
phase: "sessions.list",
attributes: {
agentId: p.agentId ?? null,
configuredAgentsOnly
}
}),
modelCatalogByAgent: preparedModelCatalogByAgent
};
const { durableStorePath, durableTargets, modelCatalogByAgent, storePath } = loaded;
const entryFilter = listFilter({
p,
loaded,
defaultsAgentId,
client,
cfg,
options
});
const selectionRuns = p.search?.trim() ? createVisibleActiveSessionRunProjector(context) : void 0;
const result = await measureDiagnosticsTimelineSpan("gateway.sessions.list.rows", () => listSessionsFromStoreAsync({
cfg,
durableStorePath,
...entryFilter ? { entryFilter } : {},
storePath,
store: loaded.store,
modelCatalog: modelCatalogByAgent,
opts: p,
...selectionRuns ? { projectActiveRun: (key, entry, agentId) => selectionRuns({
requestedKey: key,
canonicalKey: key,
sessionId: entry.sessionId,
agentId,
defaultAgentId: tryResolveSessionCompatibilityOwnerAgentId(cfg, key)
}) } : {},
...p.involvingMe === true && identityId ? { involvingActorId: identityId } : {},
...p.ownerFirst === true && identityId ? { ownerFirstActorId: identityId } : {}
}), {
config: cfg,
phase: "sessions.list"
});
result.defaults = {
...result.defaults,
modelSelectionTarget
};
const { sharingTargets, membershipKeys } = await measureDiagnosticsTimelineSpan("gateway.sessions.list.sharing", () => {
const resolvedSharingTargets = resolveSessionSharingTargets({
cfg,
targetDiscoveryCache: createGatewaySessionStoreDiscoveryCache({
cfg,
targets: durableTargets,
agentIds: result.sessions.map((session) => session.key === "global" && p.agentId ? p.agentId : resolveSessionStoreAgentId(cfg, session.key))
}),
targets: result.sessions.map((session) => ({
sessionKey: session.key,
...session.key === "global" && p.agentId ? { agentId: p.agentId } : {}
}))
});
const resolvedMembershipKeys = /* @__PURE__ */ new Set();
if (identityId && !isGatewayAdmin(client)) {
const groups = /* @__PURE__ */ new Map();
for (const target of resolvedSharingTargets) {
if (!target) continue;
const groupKey = `${target.agentId}\0${target.storePath}`;
const group = groups.get(groupKey) ?? {
agentId: target.agentId,
sessionKeys: [],
storePath: target.storePath
};
group.sessionKeys.push(target.storeKey);
groups.set(groupKey, group);
}
for (const group of groups.values()) {
const firstSessionKey = group.sessionKeys[0];
if (!firstSessionKey) continue;
for (const sessionKey of listSessionMembershipKeys({
agentId: group.agentId,
sessionKey: firstSessionKey,
storePath: group.storePath
}, group.sessionKeys, identityId)) resolvedMembershipKeys.add(`${group.agentId}\0${group.storePath}\0${sessionKey}`);
}
}
return {
sharingTargets: resolvedSharingTargets,
membershipKeys: resolvedMembershipKeys
};
}, {
config: cfg,
phase: "sessions.list",
attributes: { sessions: result.sessions.length }
});
const projectPlacement = createSessionPlacementBatchProjector(context, result.sessions);
const projectActiveRun = createVisibleActiveSessionRunProjector(context);
const sharing = prepareSessionSharing({
client,
cfg
});
measureDiagnosticsTimelineSpanSync("gateway.sessions.list.active_run_flags", () => result.sessions.forEach((session, index) => {
const sharingTarget = sharingTargets[index];
const visibility = sharingTarget ? resolveSessionVisibility(sharingTarget.entry) : "shared";
const activeRunState = projectActiveRun({
requestedKey: session.key,
canonicalKey: session.key,
sessionId: session.sessionId,
agentId: session.agentId,
defaultAgentId: tryResolveSessionCompatibilityOwnerAgentId(cfg, session.key)
});
Object.assign(session, {
visibility,
...sharingTarget ? { sharingRole: sharing.roleForTarget(sharingTarget, membershipKeys.has(`${sharingTarget.agentId}\0${sharingTarget.storePath}\0${sharingTarget.storeKey}`)) } : {},
...projectGatewaySessionActiveRun(activeRunState, session.status),
...projectPlacement(session.sessionId),
...activeRunState.runIds !== void 0 ? { activeRunIds: activeRunState.runIds } : {}
});
}), {
config: cfg,
phase: "sessions.list",
attributes: { sessions: result.sessions.length }
});
const currentVisibilityFilter = sharing.entryFilter;
const visibleSessions = currentVisibilityFilter ? result.sessions.filter((_, index) => {
const target = sharingTargets[index];
return target ? currentVisibilityFilter(target.storeKey, target.entry) : false;
}) : result.sessions;
if (visibleSessions.length !== result.sessions.length) {
const visibleKeys = new Set(visibleSessions.map((session) => session.key));
const excludedKeys = new Set(options.excludedKeys);
for (const session of result.sessions) if (!visibleKeys.has(session.key)) excludedKeys.add(session.key);
if (!options.rowRepairAttempted) return await listVisibleSessions({
...options,
excludedKeys,
loaded,
rowRepairAttempted: true
});
if (options.allowFullReload !== false) return await listVisibleSessions({ allowFullReload: false });
return {
...result,
count: visibleSessions.length,
sessions: visibleSessions
};
}
return {
...result,
sessions: visibleSessions
};
}, {
config: cfg,
phase: "sessions.list",
attributes: {
agentId: p.agentId ?? null,
configuredAgentsOnly
}
});
await respondWithCachedSessionList({
client,
config: cfg,
context,
modelCatalog: preparedModelCatalogByAgent,
request: p,
respond,
run
});
},
"sessions.cleanup": async ({ params, respond, context }) => {
if (!assertValidParams(params, validateSessionsCleanupParams, "sessions.cleanup", respond)) return;
try {
const { mode, appliedSummaries, failure } = await runSessionsCleanup({
cfg: context.getRuntimeConfig(),
opts: {
agent: params.agent,
allAgents: params.allAgents,
enforce: params.enforce,
activeKey: params.activeKey,
fixMissing: params.fixMissing,
fixDmScope: params.fixDmScope
}
});
const result = serializeSessionCleanupResult({
mode,
dryRun: false,
summaries: appliedSummaries,
failure
});
if (failure) respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, failure.message, { details: result }));
else respond(true, result, void 0);
for (const summary of appliedSummaries) {
emitSessionsChanged(context, {
reason: "cleanup",
sessionKey: void 0
});
if (summary.wouldMutate) context.logGateway.debug(`sessions.cleanup applied ${summary.storePath}: ${summary.beforeCount} -> ${summary.afterCount}`);
}
if (failure?.lifecycleCommitted) emitSessionsChanged(context, {
reason: "cleanup",
sessionKey: void 0
});
} catch (error) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, formatErrorMessage(error)));
}
},
"sessions.preview": async ({ params, respond, context, client }) => {
if (!assertValidParams(params, validateSessionsPreviewParams, "sessions.preview", respond)) return;
const keys = (Array.isArray(params.keys) ? params.keys : []).map((key) => normalizeOptionalString(key ?? "")).filter((key) => Boolean(key)).slice(0, 64);
const limit = params.limit ?? 12;
const maxChars = params.maxChars ?? 240;
if (keys.length === 0) {
respond(true, {
ts: Date.now(),
previews: []
}, void 0);
return;
}
const cfg = context.getRuntimeConfig();
const roleVisibilityFilter = hasOperatorBoundary(client, cfg) ? createSessionListEntryFilter({
client,
cfg
}) : void 0;
const previews = [];
for (const key of keys) {
if (previews.length > 0) await setImmediate();
const requestedAgent = resolveRequestedSessionAgentId(cfg, key);
if (!requestedAgent.ok) {
respond(false, void 0, requestedAgent.error);
return;
}
try {
const target = resolveGatewaySessionStoreTargetWithStore({
cfg,
key,
agentId: requestedAgent.agentId,
exactRead: true,
readOnly: true
});
const entry = resolveCanonicalSessionEntryFromStoreKeys(target.store, target.storeKeys);
if (!entry?.sessionId || roleVisibilityFilter?.(target.canonicalKey, entry) === false) {
previews.push({
key,
status: "missing",
items: []
});
continue;
}
const items = readSessionPreviewItemsFromTranscript({
agentId: target.agentId,
sessionEntry: entry,
sessionId: entry.sessionId,
sessionKey: target.canonicalKey,
storePath: target.storePath
}, limit, maxChars);
previews.push({
key,
status: items.length > 0 ? "ok" : "empty",
items
});
} catch {
previews.push({
key,
status: "error",
items: []
});
}
}
respond(true, {
ts: Date.now(),
previews
}, void 0);
},
"sessions.resolve": async ({ params, respond, context, client }) => {
if (!assertValidParams(params, validateSessionsResolveParams, "sessions.resolve", respond)) return;
const resolved = await resolveSessionKeyFromResolveParams({
cfg: context.getRuntimeConfig(),
client,
p: params
});
if (!resolved.ok) {
respond(false, void 0, resolved.error);
return;
}
if ("missing" in resolved) {
respond(true, { ok: false }, void 0);
return;
}
if ("ambiguous" in resolved) {
respond(true, {
ok: false,
candidates: resolved.candidates
}, void 0);
return;
}
respond(true, resolved, void 0);
},
...sessionByKeyReadHandlers
};
const sessionsListHandler = sessionReadHandlers["sessions.list"];
//#endregion
export { sessionsListHandler as n, sessionReadHandlers as t };