openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
251 lines (250 loc) • 12.4 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js";
import { D as tryResolveLegacyCompatibilityAgentId } from "./agent-scope-config-DcbEhP0R.js";
import { O as parseAgentSessionKey } from "./session-key-BnWWjqNc.js";
import { t as isIncognitoSessionKey } from "./incognito-session-key-BwpD1Lwd.js";
import "./legacy.default-agent-owner-BGwEdQRe.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { n as resolvePersistedSessionStoreOwnerForKey } from "./session-store-owner-CR2Ag3xK.js";
import { t as ErrorCodes } from "./gateway-error-details-w0nAGBBp.js";
import { ti as validateSessionsDeleteParams } from "./src-BiL5aQto.js";
import { d as errorShape } from "./error-codes-Bo8q2D1o.js";
import "./session-accessor-YsytfDtG.js";
import { h as runExclusiveSessionLifecycleMutation } from "./session-lifecycle-admission-CS8v45tk.js";
import { n as resolveSessionStoreAgentId } from "./session-store-key-8xEjWSNi.js";
import { a as isAgentHarnessSessionKey } from "./agent-harness-session-key-BOz3yx0-.js";
import { o as rollbackPluginOwnedSessionEntryLifecycle, r as deleteSessionEntryLifecycle } from "./session-accessor.sqlite-lifecycle-DBhNICvK.js";
import { s as isModelSelectionLocked } from "./model-overrides-Bm7jo7Di.js";
import { a as handleSessionStateSessionDeleted } from "./session-state-events-DNCKmH78.js";
import "./sessions-9nxpeTwt.js";
import { t as SESSION_LIFECYCLE_CHANGED_ERROR_REASON } from "./lifecycle-BaroCBMc.js";
import { n as resolveRequestedSessionAgentId } from "./session-request-agent-CCRSEGCB.js";
import { i as loadGatewaySessionEntryReadOnly, r as loadGatewaySessionEntry } from "./session-utils-store-CInT2loy.js";
import "./session-utils-Cai0_C6U.js";
import { r as prepareSessionWorkerPlacementRetirement } from "./session-placement-lifecycle-7DQHpaT9.js";
import { t as assertValidParams } from "./validation-pzrlzFvo.js";
import { t as resolvePluginSessionOwnershipError } from "./session-plugin-ownership-421Ior05.js";
import { n as emitSessionsChanged } from "./session-change-event-DzmH4zlz.js";
import { c as resolveGatewaySessionTargetFromKey, i as loadAccessorSessionEntryForGatewayTarget, n as isAgentMainSessionKey, o as loadSessionsRuntimeModule, s as requireSessionKey } from "./sessions-shared-D_lV07q2.js";
import { r as removeSessionWorktree } from "./session-worktree-lifecycle-_3Jyj3pK.js";
import { t as prepareSessionLifecycleDrain } from "./sessions-lifecycle-drain-B8GdcZDj.js";
//#region src/gateway/server-methods/sessions-delete.ts
var SessionDeletionError = class extends Error {
constructor(error) {
super(error.message);
this.error = error;
}
};
const sessionDeleteHandlers = { "sessions.delete": async ({ params, respond, client, context, sessionMutationAuthorization }) => {
if (!assertValidParams(params, validateSessionsDeleteParams, "sessions.delete", respond)) return;
const p = params;
const key = requireSessionKey(p.key, respond);
if (!key) return;
const cfg = context.getRuntimeConfig();
const requestedAgent = resolveRequestedSessionAgentId(cfg, key, p.agentId);
if (!requestedAgent.ok) {
respond(false, void 0, requestedAgent.error);
return;
}
const requestedAgentId = requestedAgent.agentId;
const { target, storePath } = resolveGatewaySessionTargetFromKey(key, cfg, { agentId: requestedAgentId });
const compatibilityDefaultAgentId = tryResolveLegacyCompatibilityAgentId(cfg);
const persistedStoreOwner = resolvePersistedSessionStoreOwnerForKey(cfg, key);
const protectedGlobalAgentId = persistedStoreOwner.kind === "configured" ? persistedStoreOwner.agentId : compatibilityDefaultAgentId;
const explicitlySelectedGlobalAgentId = normalizeOptionalString(p.agentId) ?? parseAgentSessionKey(key)?.agentId;
const isSelectedNonDefaultGlobal = target.canonicalKey === "global" && explicitlySelectedGlobalAgentId !== void 0 && normalizeAgentId(explicitlySelectedGlobalAgentId) !== protectedGlobalAgentId;
const isMainSession = target.canonicalKey !== "global" && isAgentMainSessionKey(cfg, target.canonicalKey);
if ((target.canonicalKey === "global" || isMainSession) && !isSelectedNonDefaultGlobal) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `Cannot delete the main session (${target.canonicalKey}).`));
return;
}
const deleteTranscript = typeof p.deleteTranscript === "boolean" ? p.deleteTranscript : true;
const initialDeleteEntry = loadGatewaySessionEntry(key, { agentId: requestedAgentId }).entry;
const expectedSessionId = p.expectedSessionId?.trim();
const expectedLifecycleRevision = p.expectedLifecycleRevision?.trim();
const sessionChangedError = () => errorShape(ErrorCodes.INVALID_REQUEST, `Session ${key} changed before deletion. Retry.`, { details: { reason: SESSION_LIFECYCLE_CHANGED_ERROR_REASON } });
const resolveEntryError = (entry) => {
const deletablePluginOwnedSession = normalizeOptionalString(entry?.pluginOwnerId) !== void 0 && entry?.agentHarnessId === void 0 && !isAgentHarnessSessionKey(target.canonicalKey);
if (isModelSelectionLocked(entry) && !deletablePluginOwnedSession) return errorShape(ErrorCodes.INVALID_REQUEST, "This session cannot be deleted while model selection is locked.");
if (p.archivedOnly === true && entry?.archivedAt === void 0) return errorShape(ErrorCodes.INVALID_REQUEST, `Session ${key} is not archived. Archive it first, then delete it.`);
if (expectedSessionId && entry?.sessionId !== expectedSessionId || expectedLifecycleRevision && entry?.lifecycleRevision !== expectedLifecycleRevision) return sessionChangedError();
return resolvePluginSessionOwnershipError({
action: "delete",
entry,
key: target.canonicalKey,
pluginOwnerId: client?.internal?.pluginRuntimeOwnerId
});
};
const initialError = resolveEntryError(initialDeleteEntry);
if (initialError) {
respond(false, void 0, initialError);
return;
}
const { cleanupSessionBeforeMutation, emitGatewaySessionEndPluginHook, emitSessionUnboundLifecycleEvent } = await loadSessionsRuntimeModule();
const assertCurrent = () => {
sessionMutationAuthorization?.assertCurrent();
const current = loadGatewaySessionEntryReadOnly(key, { agentId: requestedAgentId });
if (current.storePath !== storePath || current.canonicalKey !== target.canonicalKey || current.entry?.sessionId !== initialDeleteEntry?.sessionId || current.entry?.lifecycleRevision !== initialDeleteEntry?.lifecycleRevision) throw new SessionDeletionError(sessionChangedError());
const error = resolveEntryError(current.entry);
if (error) throw new SessionDeletionError(error);
return current;
};
const deleteLifecycleIdentities = [
target.canonicalKey,
key,
...target.storeKeys,
initialDeleteEntry?.sessionId,
expectedSessionId
];
let drain;
let deletedWorktreeId;
let worktreePreserved;
const deleteCurrent = async () => {
try {
const current = assertCurrent();
try {
drain = await prepareSessionLifecycleDrain({
action: "delete",
authorize: assertCurrent,
beforeCancel: () => {
if (p.expectedSessionUpdatedAt !== void 0 && assertCurrent().entry?.updatedAt !== p.expectedSessionUpdatedAt) throw new SessionDeletionError(sessionChangedError());
},
context,
storePath,
sessionKeys: Array.from(/* @__PURE__ */ new Set([
key,
target.canonicalKey,
...target.storeKeys
])),
sessionId: current.entry?.sessionId,
sessionKey: target.canonicalKey,
agentId: target.agentId,
defaultAgentId: compatibilityDefaultAgentId,
lifecycleIdentities: deleteLifecycleIdentities.filter((identity) => Boolean(identity))
});
} catch (error) {
assertCurrent();
if (error instanceof SessionDeletionError) throw error;
throw new SessionDeletionError(errorShape(ErrorCodes.UNAVAILABLE, `Session ${key} could not safely stop before deletion: ${formatErrorMessage(error)} Retry after active work or worker recovery finishes.`, { retryable: true }));
}
return await runExclusiveSessionLifecycleMutation({
scope: storePath,
identities: deleteLifecycleIdentities,
prepare: async () => drain?.handoffToMutation(),
finalize: async () => drain?.release(),
run: async () => {
const { entry, legacyKey, canonicalKey } = assertCurrent();
const retirement = prepareSessionWorkerPlacementRetirement({
context,
sessionId: entry?.sessionId
});
const commitGuard = () => {
assertCurrent();
retirement.assertCurrent();
if (drain?.hasAuthoritativeWork()) throw new SessionDeletionError(errorShape(ErrorCodes.UNAVAILABLE, `Session ${key} is still active; try again.`, { retryable: true }));
};
commitGuard();
const mutationCleanupError = await cleanupSessionBeforeMutation({
cfg,
key,
target,
entry,
legacyKey,
canonicalKey,
reason: "session-delete",
assertCurrent: commitGuard
});
if (mutationCleanupError) throw new SessionDeletionError(mutationCleanupError);
const postCleanupTarget = loadAccessorSessionEntryForGatewayTarget({
key,
cfg,
...requestedAgentId ? { agentId: requestedAgentId } : {}
});
const postCleanupEntry = postCleanupTarget.entry;
deletedWorktreeId = normalizeOptionalString(postCleanupEntry?.worktree?.id);
commitGuard();
const pluginOwnerId = normalizeOptionalString(postCleanupEntry?.pluginOwnerId);
const incognito = postCleanupEntry?.incognito === true || isIncognitoSessionKey(target.canonicalKey);
const deletionParams = {
agentId: target.agentId,
archiveTranscript: incognito ? false : deleteTranscript,
commitGuard,
deleteDeliveryArtifacts: true,
deleteTranscriptWithoutArchive: incognito,
expectedEntry: postCleanupEntry,
expectedLifecycleRevision,
expectedSessionId: initialDeleteEntry?.sessionId ?? null,
expectedUpdatedAt: postCleanupEntry?.updatedAt,
storePath,
target: {
canonicalKey: target.canonicalKey,
storeKeys: target.storeKeys
}
};
const result = postCleanupEntry && pluginOwnerId && isModelSelectionLocked(postCleanupEntry) ? await rollbackPluginOwnedSessionEntryLifecycle({
...deletionParams,
expectedEntry: postCleanupEntry,
expectedPluginOwnerId: pluginOwnerId,
target: {
canonicalKey: postCleanupTarget.target.canonicalKey,
storeKeys: postCleanupTarget.target.storeKeys
}
}) : await deleteSessionEntryLifecycle(deletionParams);
if (result.expectedEntryMismatch) throw new SessionDeletionError(sessionChangedError());
if (result.deleted) {
retirement.retire();
emitGatewaySessionEndPluginHook({
cfg,
sessionKey: target.canonicalKey ?? key,
sessionId: result.deletedSessionId,
storePath,
agentId: target.agentId,
reason: "deleted",
archivedTranscripts: result.archivedTranscripts
});
await emitSessionUnboundLifecycleEvent({
targetSessionKey: target.canonicalKey ?? key,
reason: "session-delete",
emitHooks: p.emitLifecycleHooks !== false
});
const deletedSessionKey = target.canonicalKey ?? key;
handleSessionStateSessionDeleted(deletedSessionKey, requestedAgentId ?? resolveSessionStoreAgentId(cfg, deletedSessionKey));
worktreePreserved = await removeSessionWorktree({
id: deletedWorktreeId,
sessionKey: deletedSessionKey,
reason: "session-delete"
});
}
return result;
}
});
} finally {
drain?.release();
}
};
const deletion = await deleteCurrent().catch((error) => {
if (!(error instanceof SessionDeletionError)) throw error;
respond(false, void 0, error.error);
});
if (!deletion) return;
const deleted = deletion.deleted;
const archived = deletion.archivedTranscripts.map((entryLocal) => entryLocal.archivedPath);
respond(true, {
ok: true,
key: target.canonicalKey,
deleted,
archived,
...worktreePreserved ? { worktreePreserved } : {}
}, void 0);
if (deleted) {
emitSessionsChanged(context, {
sessionKey: target.canonicalKey,
sessionId: deletion.deletedSessionId,
agentId: target.agentId,
reason: "delete"
});
emitSessionsChanged(context, { reason: "delete" });
}
} };
//#endregion
export { sessionDeleteHandlers as t };