openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
158 lines (157 loc) • 7.99 kB
JavaScript
import { k as withTimeout } from "./fs-safe-B6pvPGnf.js";
import { f as isCompetingSessionWorkAdmissionActive, g as startSessionWorkAdmissionInterruption, h as runExclusiveSessionLifecycleMutation, i as closeSessionWorkAdmissions, t as SESSION_WORK_ADMISSION_DRAIN_TIMEOUT_MS } from "./session-lifecycle-admission-CS8v45tk.js";
import { s as createAgentRunDirectAbortError } from "./run-termination-jabvTOp5.js";
import { h as replyRunRegistry, n as abortReplyRunBySessionId, u as isReplyRunActiveForSessionId, x as waitForReplyRunEndBySessionId } from "./reply-run-registry.registry-BMo0d96L.js";
import "./reply-run-registry-BlmBKV23.js";
import { N as waitForEmbeddedAgentRunEnd, f as isEmbeddedAgentRunInProgress, n as abortEmbeddedAgentRun } from "./runs-Cb42qain.js";
import { a as getCommandLaneSnapshot } from "./command-queue-C3Fv2rcU.js";
import { s as hasPendingFollowupQueueWork } from "./settings-jmDsm5RI.js";
import { t as resolveEmbeddedSessionLane } from "./lanes-CVttd5qX.js";
import { t as clearSessionQueues } from "./cleanup-DKkilV3a.js";
import { i as prepareSessionWorkerPlacementStop, n as prepareSessionWorkerPlacementMutationCheck } from "./session-placement-lifecycle-7DQHpaT9.js";
import { m as waitForChatAbortControllerRemoval } from "./chat-abort-qLn3eFOg.js";
import { o as hasGatewaySessionAbortOwner, t as abortChatRunsForSessionKeyWithPartials } from "./chat-abort-runtime-CKI3fkKr.js";
import { t as createChatAbortOps } from "./chat-abort-ops-zvMvu0-3.js";
import { t as asWorkerInferenceControl } from "./inference-control-CDvM08Nt.js";
import { t as beginWorkerInferenceSessionDrain } from "./inference-control-internal-CQh2JfZC.js";
//#region src/gateway/server-methods/sessions-lifecycle-drain.ts
function hasAuthoritativeSessionWork(params, workerDrain, terminalDrain, workIdentities) {
const sessionId = params.sessionId;
return isCompetingSessionWorkAdmissionActive(params.storePath, params.lifecycleIdentities) || params.sessionKeys.some((key) => replyRunRegistry.isActive(key)) || Boolean(sessionId && isReplyRunActiveForSessionId(sessionId)) || Boolean(sessionId && isEmbeddedAgentRunInProgress(sessionId)) || hasPendingFollowupQueueWork(workIdentities) || workIdentities.some((key) => getCommandLaneSnapshot(resolveEmbeddedSessionLane(key)).queuedCount > 0) || hasGatewaySessionAbortOwner({
context: params.context,
sessionKeys: params.sessionKeys,
sessionId,
agentId: params.agentId,
defaultAgentId: params.defaultAgentId
}) || Boolean(sessionId && params.context.workerSessionPlacementService?.getMany([sessionId]).get(sessionId)?.turnClaim) || workerDrain?.hasWork() === true || terminalDrain?.hasWork() === true;
}
/** Drain outside mutation locks; retain the closure until the final mutation owns ingress. */
async function prepareSessionLifecycleDrain(params) {
const timeoutMs = SESSION_WORK_ADMISSION_DRAIN_TIMEOUT_MS;
const workIdentities = Array.from(/* @__PURE__ */ new Set([...params.sessionKeys, ...params.sessionId ? [params.sessionId] : []]));
const workerService = params.context.workerEnvironmentService;
const workerControl = asWorkerInferenceControl(workerService);
let workerDrain;
let terminalDrain;
let releaseAdmissions = () => {};
let released = false;
const release = () => {
if (released) return;
released = true;
try {
terminalDrain?.release();
} finally {
try {
workerDrain?.release();
} finally {
releaseAdmissions();
}
}
};
try {
const prepared = await runExclusiveSessionLifecycleMutation({
scope: params.storePath,
identities: params.lifecycleIdentities,
run: async () => {
params.authorize?.();
params.beforeCancel?.();
const reclaim = prepareSessionWorkerPlacementStop(params);
releaseAdmissions = closeSessionWorkAdmissions({
scope: params.storePath,
identities: params.lifecycleIdentities,
reason: createAgentRunDirectAbortError()
});
if (params.sessionId) {
workerDrain = beginWorkerInferenceSessionDrain(workerService, params.sessionId);
if (!workerDrain && workerControl?.hasInferenceForSession(params.sessionId) === true) throw new Error("Worker inference drain is unavailable");
terminalDrain = params.context.terminalSessions?.beginAgentSessionDrain({
kind: "agent",
agentSessionKey: params.sessionKey,
agentSessionId: params.sessionId,
agentId: params.agentId
});
}
let controllerDrain = Promise.resolve(true);
const cancellation = abortChatRunsForSessionKeyWithPartials({
context: params.context,
ops: createChatAbortOps(params.context),
sessionKey: params.sessionKeys[0],
sessionKeyAliases: params.sessionKeys.slice(1),
sessionId: params.sessionId,
agentId: params.agentId,
defaultAgentId: params.defaultAgentId,
abortOrigin: "rpc",
stopReason: params.action,
requester: { isAdmin: true },
includeProtectedRuns: true,
onControllerTargets: (targets) => {
controllerDrain = waitForChatAbortControllerRemoval({
entries: params.context.chatAbortControllers,
targets,
timeoutMs
});
},
onAuthorizedAfterQueuedAbort: () => {
const cleared = clearSessionQueues(workIdentities);
let aborted = cleared.followupCleared > 0 || cleared.laneCleared > 0;
for (const key of params.sessionKeys) aborted = replyRunRegistry.abort(key) || aborted;
if (params.sessionId) {
aborted = abortReplyRunBySessionId(params.sessionId) || aborted;
aborted = abortEmbeddedAgentRun(params.sessionId) || aborted;
}
return aborted;
}
});
cancellation.catch(() => {});
return {
reclaim,
cancellation,
controllerDrain
};
}
});
if ((await prepared.cancellation).unauthorized) throw new Error("Session cancellation lost ownership");
params.authorize?.();
const { released: admittedWork } = startSessionWorkAdmissionInterruption({
scope: params.storePath,
identities: params.lifecycleIdentities
});
const replyWork = Promise.all([...params.sessionKeys.map((key) => replyRunRegistry.waitForIdle(key, timeoutMs)), ...params.sessionId ? [waitForReplyRunEndBySessionId(params.sessionId, timeoutMs)] : []]).then((results) => results.every(Boolean));
const embeddedWork = params.sessionId ? waitForEmbeddedAgentRunEnd(params.sessionId, timeoutMs) : Promise.resolve(true);
const placementService = params.context.workerSessionPlacementService;
const placementWork = (params.sessionId ? placementService?.getMany([params.sessionId]).get(params.sessionId) : void 0)?.turnClaim ? placementService?.waitForTurnClaimRelease ? placementService.waitForTurnClaimRelease(params.sessionId, { timeoutMs }).then(() => true) : Promise.resolve(false) : Promise.resolve(true);
const workerWork = workerDrain ? withTimeout(workerDrain.drained, timeoutMs, "worker inference lifecycle drain").then(() => true) : Promise.resolve(true);
const terminalWork = terminalDrain ? withTimeout(terminalDrain.drained, timeoutMs, "agent terminal lifecycle drain").then(() => true) : Promise.resolve(true);
if (!(await Promise.all([
prepared.controllerDrain,
replyWork,
embeddedWork,
placementWork,
workerWork,
terminalWork
])).every(Boolean)) throw new Error("Session work is still active after the lifecycle drain");
await prepared.reclaim();
await withTimeout(admittedWork, timeoutMs, "session work admission lifecycle drain");
const assertPlacementCurrent = prepareSessionWorkerPlacementMutationCheck({
context: params.context,
sessionId: params.sessionId
});
return {
handoffToMutation: () => releaseAdmissions(),
release,
hasAuthoritativeWork: () => {
try {
assertPlacementCurrent();
} catch {
return true;
}
return hasAuthoritativeSessionWork(params, workerDrain, terminalDrain, workIdentities);
}
};
} catch (error) {
release();
throw error;
}
}
//#endregion
export { prepareSessionLifecycleDrain as t };