openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
269 lines (268 loc) • 11.6 kB
JavaScript
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 };