UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

269 lines (268 loc) 11.6 kB
import { t as ErrorCodes } from "./gateway-error-details-w0nAGBBp.js"; import { Jr as validateSessionsCompactionBranchParams, Xr as validateSessionsCompactionRestoreParams } from "./src-BiL5aQto.js"; import { d as errorShape } from "./error-codes-Bo8q2D1o.js"; import "./users-CPWrgxrZ.js"; import { d as interruptSessionWorkAdmissions, h as runExclusiveSessionLifecycleMutation } from "./session-lifecycle-admission-CS8v45tk.js"; import { N as waitForEmbeddedAgentRunEnd, n as abortEmbeddedAgentRun, u as isEmbeddedAgentRunActive } from "./runs-Cb42qain.js"; import "./sessions-9nxpeTwt.js"; import { t as SESSION_LIFECYCLE_CHANGED_ERROR_REASON } from "./lifecycle-BaroCBMc.js"; import { a as resolveCreatorSandbox, f as resolveOperatorSessionCreation, t as authorizeGatewaySessionCreation } from "./operator-role-policy-wsr1DeJv.js"; import { i as tryResolveSessionCompatibilityOwnerAgentId, n as resolveRequestedSessionAgentId } from "./session-request-agent-CCRSEGCB.js"; import { t as clearSessionQueues } from "./cleanup-DKkilV3a.js"; import { a as resolveSessionWorkerPlacementMutationError } from "./session-placement-lifecycle-7DQHpaT9.js"; import { n as getSessionCompactionCheckpoint, t as createFileBackedCompactionCheckpointStore } from "./session-compaction-checkpoints-D4Jo2sbX.js"; import { t as assertValidParams } from "./validation-pzrlzFvo.js"; import { r as hasTrackedActiveSessionRun } from "./session-active-runs-CCNuJW7B.js"; import { n as emitSessionsChanged } from "./session-change-event-DzmH4zlz.js"; import { t as asWorkerInferenceControl } from "./inference-control-CDvM08Nt.js"; import { i as loadAccessorSessionEntryForGatewayTarget, s as requireSessionKey } from "./sessions-shared-D_lV07q2.js"; import { n as handleChatAbortRequestWithLifecycle } from "./chat-abort-handler-Cs4t_nYG.js"; import { t as buildDashboardSessionKey } from "./session-create-service-CM4MxLMO.js"; import { t as resolveAbortSessionKey } from "./sessions-abort-Ca3ZJka3.js"; //#region src/gateway/server-methods/session-run-interruption.ts /** Hard-stop session work for lifecycle mutation callers that have already fenced admission. */ async function interruptSessionRunIfActive(params) { const cfg = params.context.getRuntimeConfig(); const hasTrackedRun = hasTrackedActiveSessionRun({ context: params.context, requestedKey: params.requestedKey, canonicalKey: params.canonicalKey, agentId: params.agentId, defaultAgentId: tryResolveSessionCompatibilityOwnerAgentId(cfg, params.canonicalKey), excludeRunIds: params.excludeRunIds }); const hasEmbeddedRun = typeof params.sessionId === "string" && params.sessionId ? isEmbeddedAgentRunActive(params.sessionId) : false; const hasWorkerRun = typeof params.sessionId === "string" && params.sessionId ? asWorkerInferenceControl(params.context.workerEnvironmentService)?.hasInferenceForSession(params.sessionId) ?? false : false; if (!hasTrackedRun && !hasEmbeddedRun && !hasWorkerRun) return { interrupted: false }; if (hasTrackedRun || hasWorkerRun) { let abortOk = true; let abortError; const abortSessionKey = resolveAbortSessionKey({ context: params.context, requestedKey: params.requestedKey, canonicalKey: params.canonicalKey, agentId: params.agentId, defaultAgentId: tryResolveSessionCompatibilityOwnerAgentId(cfg, params.canonicalKey) }); await handleChatAbortRequestWithLifecycle({ req: params.req, params: { sessionKey: abortSessionKey, ...params.agentId ? { agentId: params.agentId } : {} }, respond: (ok, _payload, error) => { abortOk = ok; abortError = error; }, context: params.context, client: params.client, isWebchatConnect: params.isWebchatConnect }, params.excludeRunIds ? { excludeRunIds: params.excludeRunIds } : {}); if (!abortOk) return { interrupted: true, error: abortError ?? errorShape(ErrorCodes.UNAVAILABLE, "failed to interrupt active session") }; } if (hasEmbeddedRun && params.sessionId) abortEmbeddedAgentRun(params.sessionId); clearSessionQueues([ params.requestedKey, params.canonicalKey, params.sessionId ]); if (hasEmbeddedRun && params.sessionId) { if (!await waitForEmbeddedAgentRunEnd(params.sessionId, 15e3)) return { interrupted: true, error: errorShape(ErrorCodes.UNAVAILABLE, `Session ${params.requestedKey} is still active; try again in a moment.`) }; } return { interrupted: true }; } //#endregion //#region src/gateway/server-methods/sessions-compaction-checkpoints.ts const compactionCheckpointStore = createFileBackedCompactionCheckpointStore(); const MODEL_SELECTION_LOCKED_CHECKPOINT_MESSAGE = "Checkpoint branch and restore are unavailable while model selection is locked."; function checkpointConflict(key, action) { return errorShape(ErrorCodes.INVALID_REQUEST, `Session ${key} changed before checkpoint ${action}. Retry.`, { details: { reason: SESSION_LIFECYCLE_CHANGED_ERROR_REASON } }); } function createCheckpointHandler(action) { const validate = action === "branch" ? validateSessionsCompactionBranchParams : validateSessionsCompactionRestoreParams; return async ({ req, params, respond, context, client, isWebchatConnect }) => { if (!assertValidParams(params, validate, `sessions.compaction.${action}`, respond)) return; const fail = (error) => respond(false, void 0, typeof error === "string" ? errorShape(ErrorCodes.INVALID_REQUEST, error) : error); const key = requireSessionKey(params.key, respond); if (!key) return; const checkpointId = params.checkpointId.trim(); if (!checkpointId) return fail("checkpointId required"); const cfg = context.getRuntimeConfig(); const requestedAgent = resolveRequestedSessionAgentId(cfg, key, params.agentId); if (!requestedAgent.ok) return fail(requestedAgent.error); const { entry, canonicalKey, sessionStoreKey, target, storePath } = loadAccessorSessionEntryForGatewayTarget({ key, cfg, agentId: requestedAgent.agentId }); if (!entry?.sessionId) return fail(`session not found: ${key}`); if (!getSessionCompactionCheckpoint({ entry, checkpointId })) return fail(`checkpoint not found: ${checkpointId}`); const complete = (result, sourceKey) => { switch (result.status) { case "missing-checkpoint": case "missing-boundary": return fail(`checkpoint not found: ${checkpointId}`); case "missing-session": return fail(`session not found: ${key}`); case "model-selection-locked": return fail(MODEL_SELECTION_LOCKED_CHECKPOINT_MESSAGE); case "conflict": return fail(checkpointConflict(key, action)); case "failed": return fail(errorShape(ErrorCodes.UNAVAILABLE, action === "branch" ? "failed to create checkpoint branch transcript" : "failed to restore checkpoint transcript")); case "created": break; default: return result; } respond(true, { ok: true, ...action === "branch" ? { sourceKey } : {}, key: result.key, sessionId: result.entry.sessionId, checkpoint: result.checkpoint, entry: result.entry }, void 0); emitSessionsChanged(context, { sessionKey: sourceKey, agentId: requestedAgent.agentId, reason: `checkpoint-${action}` }); if (action === "branch") emitSessionsChanged(context, { sessionKey: result.key, reason: "checkpoint-branch" }); }; if (action === "branch") { const creationError = authorizeGatewaySessionCreation({ cfg, client, agentId: target.agentId }); if (creationError) return fail(creationError); const nextKey = buildDashboardSessionKey(target.agentId); const creation = resolveOperatorSessionCreation(client); const sandbox = creation.actor?.id === "gateway-owner" ? entry.sandbox : resolveCreatorSandbox(cfg, creation); return complete(await compactionCheckpointStore.branchCheckpointSession({ agentId: target.agentId, expectedState: { sessionId: entry.sessionId, lifecycleRevision: entry.lifecycleRevision }, storePath, sourceKey: canonicalKey, sourceStoreKey: sessionStoreKey, nextKey, checkpointId, ...creation.actor ? { creation: { ...creation, sandbox } } : {} }), canonicalKey); } const initialPlacementError = resolveSessionWorkerPlacementMutationError({ action: "restore", context, key, sessionId: entry.sessionId }); if (initialPlacementError) return fail(initialPlacementError.message); const lifecycleIdentities = [ key, canonicalKey, sessionStoreKey, entry.sessionId, entry.lifecycleRevision ]; let preparationError; await runExclusiveSessionLifecycleMutation({ scope: storePath, identities: [entry.sessionId, entry.lifecycleRevision], prepare: async () => { const current = loadAccessorSessionEntryForGatewayTarget({ key, cfg, agentId: requestedAgent.agentId }); const currentCheckpoint = current.entry ? getSessionCompactionCheckpoint({ entry: current.entry, checkpointId }) : void 0; if (current.entry?.sessionId !== entry.sessionId || current.entry.lifecycleRevision !== entry.lifecycleRevision || !currentCheckpoint) { preparationError = checkpointConflict(key, "restore"); return; } if (current.entry.modelSelectionLocked === true) { preparationError = errorShape(ErrorCodes.INVALID_REQUEST, MODEL_SELECTION_LOCKED_CHECKPOINT_MESSAGE); return; } const placementError = resolveSessionWorkerPlacementMutationError({ action: "restore", context, key, sessionId: current.entry.sessionId }); if (placementError) { preparationError = errorShape(ErrorCodes.INVALID_REQUEST, placementError.message); return; } clearSessionQueues([ key, current.canonicalKey, current.sessionStoreKey, current.entry.sessionId ]); if (!await interruptSessionWorkAdmissions({ scope: storePath, identities: lifecycleIdentities, timeoutMs: 15e3 })) preparationError = errorShape(ErrorCodes.UNAVAILABLE, `Session ${key} is still active; try again.`); }, run: async () => { if (preparationError) return fail(preparationError); const current = loadAccessorSessionEntryForGatewayTarget({ key, cfg, agentId: requestedAgent.agentId }); if (!current.entry?.sessionId) return fail(`session not found: ${key}`); if (current.entry.modelSelectionLocked === true) return fail(MODEL_SELECTION_LOCKED_CHECKPOINT_MESSAGE); if (!getSessionCompactionCheckpoint({ entry: current.entry, checkpointId })) return fail(`checkpoint not found: ${checkpointId}`); const interruptResult = await interruptSessionRunIfActive({ req, context, client, isWebchatConnect, requestedKey: key, canonicalKey: current.canonicalKey, agentId: requestedAgent.agentId, sessionId: current.entry.sessionId }); if (interruptResult.error) return fail(interruptResult.error); const result = await compactionCheckpointStore.restoreCheckpointSession({ agentId: requestedAgent.agentId, expectedState: { sessionId: current.entry.sessionId, lifecycleRevision: current.entry.lifecycleRevision }, storePath, sessionKey: current.canonicalKey, sessionStoreKey: current.sessionStoreKey, checkpointId }); complete(result, current.canonicalKey); } }); }; } const sessionCheckpointHandlers = { "sessions.compaction.branch": createCheckpointHandler("branch"), "sessions.compaction.restore": createCheckpointHandler("restore") }; //#endregion export { sessionCheckpointHandlers };