openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
340 lines (339 loc) • 17.2 kB
JavaScript
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js";
import { O as parseAgentSessionKey, c as classifySessionKeyShape, l as isUnscopedSessionKeySentinel } from "./session-key-BnWWjqNc.js";
import { a as normalizeOptionalAgentRuntimeId } from "./agent-runtime-id-BpfvnoN1.js";
import { E as resolveSessionStorePathForScope } from "./session-accessor.sqlite-entry-CWk3jL7s.js";
import { i as sqliteSessionFileMarkerMatchesTarget, n as parseSqliteSessionFileMarker } from "./legacy-sqlite-marker-COPKCuIN.js";
import { a as resolveSessionEntryAccessTarget } from "./session-accessor.entry-CQ2WPpfn.js";
import { a as isAgentHarnessSessionKey, h as resolveSessionPinnedHarnessId, o as isAgentHarnessSessionKeyOwnedBy } from "./agent-harness-session-key-BOz3yx0-.js";
import { i as resolveSessionRuntimeOverrideForProvider } from "./session-runtime-compat-DNAICzi6.js";
import { r as resolveInitialEmbeddedRunModel } from "./runtime-resolution-C4zNdmQC.js";
//#region src/plugins/registry-runtime-session-ownership.ts
const PLUGIN_GATEWAY_SESSION_MUTATION_METHODS = /* @__PURE__ */ new Set([
"agent",
"chat.abort",
"chat.inject",
"chat.send",
"message.action",
"plugins.sessionAction",
"send",
"sessions.abort",
"sessions.compact",
"sessions.compaction.branch",
"sessions.compaction.restore",
"sessions.branches.switch",
"sessions.rewind",
"sessions.fork",
"sessions.create",
"sessions.delete",
"sessions.patchMany",
"sessions.patch",
"sessions.pluginPatch",
"sessions.reset",
"sessions.send",
"sessions.steer",
"wake"
]);
const PLUGIN_GATEWAY_GLOBAL_SESSION_MUTATION_METHODS = /* @__PURE__ */ new Set([
"sessions.cleanup",
"sessions.groups.delete",
"sessions.groups.rename"
]);
/** Session ownership checks loaded only when a plugin invokes an async action. */
function createPluginSessionOwnership(state, pluginId) {
const { registry, registryParams } = state;
const currentSessionConfig = () => registryParams.runtime.config.current();
const resolveHarnessRegistration = (harnessId) => {
const normalizedHarnessId = normalizeOptionalAgentRuntimeId(harnessId);
return normalizedHarnessId ? registry.agentHarnesses.find((entry) => normalizeOptionalAgentRuntimeId(entry.harness.id) === normalizedHarnessId) : void 0;
};
const resolveHarnessRegistrationForSessionKey = (sessionKey) => registry.agentHarnesses.find((entry) => {
const rawHarnessId = normalizeOptionalString(entry.harness.id)?.toLowerCase();
return rawHarnessId === normalizeOptionalAgentRuntimeId(rawHarnessId) && isAgentHarnessSessionKeyOwnedBy(sessionKey, rawHarnessId);
});
const assertOwnedHarness = (harnessId, action) => {
const normalizedHarnessId = normalizeOptionalAgentRuntimeId(harnessId);
if (!normalizedHarnessId) throw new Error(`Plugin "${pluginId}" must provide a registered agent harness id to ${action}.`);
const registration = resolveHarnessRegistration(normalizedHarnessId);
if (!registration) throw new Error(`Plugin "${pluginId}" must register agent harness "${normalizedHarnessId}" before it can ${action}.`);
if (registration.pluginId !== pluginId) throw new Error(`Agent harness "${normalizedHarnessId}" is owned by plugin "${registration.pluginId}", not "${pluginId}".`);
return normalizedHarnessId;
};
const assertReservedSessionKeyOwned = (sessionKey, action) => {
const normalizedSessionKey = normalizeOptionalString(sessionKey);
if (!normalizedSessionKey || !isAgentHarnessSessionKey(normalizedSessionKey)) return;
const registration = resolveHarnessRegistrationForSessionKey(normalizedSessionKey);
if (!registration) throw new Error(`Plugin "${pluginId}" cannot ${action} reserved agent harness session "${normalizedSessionKey}" because its harness is not registered.`);
if (registration.pluginId !== pluginId) throw new Error(`Plugin "${pluginId}" cannot ${action} reserved agent harness session "${normalizedSessionKey}" owned by plugin "${registration.pluginId}".`);
};
const resolveLockedSessionHarnessRegistration = (sessionKey, entry, action) => {
if (entry.modelSelectionLocked !== true) return;
const pluginOwnerId = normalizeOptionalString(entry.pluginOwnerId);
if (pluginOwnerId) {
if (isAgentHarnessSessionKey(sessionKey)) throw new Error(`Locked session "${sessionKey}" mixes plugin and reserved harness ownership.`);
return { ownerPluginId: pluginOwnerId };
}
const harnessId = resolveSessionPinnedHarnessId(entry);
if (!harnessId) throw new Error(`Plugin "${pluginId}" must provide a registered agent harness id to ${action} locked sessions.`);
const registration = resolveHarnessRegistration(harnessId);
if (!registration) throw new Error(`Plugin "${pluginId}" must register agent harness "${harnessId}" before it can ${action} locked sessions.`);
if (isAgentHarnessSessionKey(sessionKey) && !isAgentHarnessSessionKeyOwnedBy(sessionKey, harnessId)) throw new Error(`Locked session "${sessionKey}" belongs to agent harness "${harnessId}", which does not match its reserved session key.`);
return {
ownerPluginId: registration.pluginId,
harnessId,
registration
};
};
const assertLockedSessionEntryOwned = (sessionKey, entry, action) => {
const resolved = resolveLockedSessionHarnessRegistration(sessionKey, entry, action);
if (!resolved) return;
if (resolved.ownerPluginId !== pluginId) throw new Error(`Locked session "${sessionKey}" is owned by plugin "${resolved.ownerPluginId}", not "${pluginId}".`);
};
const assertSessionEntryOwned = (params) => {
if (params.entry) {
assertLockedSessionEntryOwned(params.sessionKey, params.entry, params.action);
return;
}
assertReservedSessionKeyOwned(params.sessionKey, params.action);
};
const resolveStoredSessionOwnershipTarget = (params) => {
if (classifySessionKeyShape(params.sessionKey) === "legacy_or_alias" && !isUnscopedSessionKeySentinel(params.sessionKey) && params.agentId === void 0 && params.storePath === void 0) {
const target = resolveSessionEntryAccessTarget({
cfg: currentSessionConfig(),
sessionKey: params.sessionKey,
...params.env !== void 0 ? { env: params.env } : {}
});
return {
entry: target.entry,
sessionKey: target.canonicalKey
};
}
return {
entry: registryParams.runtime.agent.session.getSessionEntry({
sessionKey: params.sessionKey,
readConsistency: "latest",
...params.agentId !== void 0 ? { agentId: params.agentId } : {},
...params.env !== void 0 ? { env: params.env } : {},
...params.storePath !== void 0 ? { storePath: params.storePath } : {}
}),
sessionKey: params.sessionKey
};
};
const assertStoredSessionEntryOwned = (params) => {
const target = resolveStoredSessionOwnershipTarget(params);
assertSessionEntryOwned({
action: params.action,
...target
});
return target.entry;
};
const resolveStoredSessionExecutionOwner = (params) => {
const target = resolveStoredSessionOwnershipTarget(params);
const { entry, sessionKey } = target;
const locked = entry ? resolveLockedSessionHarnessRegistration(sessionKey, entry, params.action) : void 0;
if (!entry || !locked || locked.ownerPluginId === pluginId) {
assertSessionEntryOwned({
action: params.action,
...target
});
return;
}
const registration = "registration" in locked ? locked.registration : void 0;
if (!registration) throw new Error(`Locked session "${sessionKey}" is owned by plugin "${locked.ownerPluginId}", not "${pluginId}".`);
if (!registration.harness.delegatedExecutionPluginIds?.includes(pluginId)) assertLockedSessionEntryOwned(sessionKey, entry, params.action);
return locked.ownerPluginId;
};
const assertSessionIdentitiesOwned = (params) => {
const agentId = normalizeOptionalString(params.agentId);
const storePath = normalizeOptionalString(params.storePath);
const sessionKeys = /* @__PURE__ */ new Set();
for (const value of params.sessionKeys ?? []) {
const sessionKey = normalizeOptionalString(value);
if (sessionKey) sessionKeys.add(sessionKey);
}
for (const sessionKey of sessionKeys) assertStoredSessionEntryOwned({
action: params.action,
sessionKey,
...agentId ? { agentId } : {},
...storePath ? { storePath } : {}
});
const sessionIds = /* @__PURE__ */ new Set();
for (const value of params.sessionIds ?? []) {
const sessionId = normalizeOptionalString(value);
if (sessionId) sessionIds.add(sessionId);
}
const sessionFiles = /* @__PURE__ */ new Set();
for (const value of params.sessionFiles ?? []) {
const sessionFile = normalizeOptionalString(value);
if (sessionFile) sessionFiles.add(sessionFile);
}
if (sessionIds.size === 0 && sessionFiles.size === 0) return;
const entries = registryParams.runtime.agent.session.listSessionEntries({
...agentId ? { agentId } : {},
...storePath ? { storePath } : {},
readOnly: true
});
for (const { sessionKey, entry } of entries) if (sessionIds.has(entry.sessionId)) assertSessionEntryOwned({
action: params.action,
entry,
sessionKey
});
for (const sessionFile of sessionFiles) {
const sessionKeyMatches = entries.filter(({ sessionKey }) => sessionKey === sessionFile);
if (sessionKeyMatches.length > 0) {
for (const match of sessionKeyMatches) assertSessionEntryOwned({
action: params.action,
entry: match.entry,
sessionKey: match.sessionKey
});
const matchedSessionIds = new Set(sessionKeyMatches.map(({ entry }) => normalizeOptionalString(entry.sessionId)).filter((sessionId) => Boolean(sessionId)));
for (const match of entries) {
const matchSessionId = normalizeOptionalString(match.entry.sessionId);
if (matchSessionId && matchedSessionIds.has(matchSessionId)) assertSessionEntryOwned({
action: params.action,
entry: match.entry,
sessionKey: match.sessionKey
});
}
continue;
}
const marker = parseSqliteSessionFileMarker(sessionFile);
if (!marker) throw new Error("Plugin session ownership checks require a SQLite transcript marker.");
const matches = registryParams.runtime.agent.session.listSessionEntries({
agentId: marker.agentId,
storePath: marker.storePath,
readOnly: true
}).filter(({ entry }) => entry.sessionId === marker.sessionId);
if (matches.length === 0) throw new Error(`Plugin session ownership target not found: ${marker.sessionId}`);
for (const match of matches) assertSessionEntryOwned({
action: params.action,
entry: match.entry,
sessionKey: match.sessionKey
});
}
};
const prepareRunSessionExecution = (params) => {
const target = params.sessionTarget;
const targetSessionKey = normalizeOptionalString(target?.sessionKey);
const directSessionKey = normalizeOptionalString(params.sessionKey);
if (targetSessionKey && directSessionKey && targetSessionKey !== directSessionKey) throw new Error("Delegated agent execution requires one exact session key.");
const sessionKey = targetSessionKey ?? directSessionKey;
const storePath = normalizeOptionalString(target?.storePath);
const agentId = normalizeOptionalString(target?.agentId ?? params.agentId);
const sessionKeyAgentId = parseAgentSessionKey(sessionKey)?.agentId;
const normalizedAgentId = agentId ? normalizeAgentId(agentId) : void 0;
if (sessionKeyAgentId && normalizedAgentId && normalizedAgentId !== sessionKeyAgentId) throw new Error(`Plugin session ownership agent "${normalizedAgentId}" does not match session key agent "${sessionKeyAgentId}".`);
const ownershipAgentId = sessionKeyAgentId ?? normalizedAgentId;
const ownershipStorePath = sessionKey && sessionKeyAgentId ? resolveSessionStorePathForScope({
agentId: sessionKeyAgentId,
sessionKey,
...storePath ? { storePath } : {}
}) : storePath;
const entry = sessionKey ? registryParams.runtime.agent.session.getSessionEntry({
sessionKey,
readConsistency: "latest",
...agentId ? { agentId } : {},
...storePath ? { storePath } : {}
}) : void 0;
const targetSessionId = normalizeOptionalString(target?.sessionId);
const targetAgentId = normalizeOptionalString(target?.agentId);
const directSessionId = normalizeOptionalString(params.sessionId);
const directAgentId = normalizeOptionalString(params.agentId);
const sessionFile = normalizeOptionalString(params.sessionFile);
if (target) {
const legacySessionIdentityMatches = Boolean(sessionFile) && Boolean(agentId) && Boolean(storePath) && Boolean(entry?.sessionId) && sqliteSessionFileMarkerMatchesTarget(sessionFile, {
agentId,
sessionId: entry.sessionId,
storePath
});
if (!(targetSessionKey === sessionKey && Boolean(storePath) && Boolean(entry) && targetSessionId === entry?.sessionId && directSessionId === entry?.sessionId && targetAgentId === directAgentId && (!sessionFile || sessionFile === sessionKey || legacySessionIdentityMatches))) throw new Error(`Plugin "${pluginId}" may execute a persisted session only with its exact session target identity.`);
}
const locked = sessionKey && entry ? resolveLockedSessionHarnessRegistration(sessionKey, entry, "run") : void 0;
const ownerPluginId = locked?.ownerPluginId;
if (locked && entry && sessionKey && ownerPluginId !== pluginId) {
const registration = "registration" in locked ? locked.registration : void 0;
if (!registration) throw new Error(`Locked session "${sessionKey}" is owned by plugin "${locked.ownerPluginId}", not "${pluginId}".`);
if (!registration.harness.delegatedExecutionPluginIds?.includes(pluginId)) assertLockedSessionEntryOwned(sessionKey, entry, "run");
const requestedHarnessId = normalizeOptionalAgentRuntimeId(params.agentHarnessId);
const requestedRuntimeOverride = normalizeOptionalAgentRuntimeId(params.agentHarnessRuntimeOverride);
const identityMatches = Boolean(target) && targetSessionId === entry.sessionId && directSessionId === entry.sessionId;
const harnessMatches = params.modelSelectionLocked === true && requestedHarnessId === locked.harnessId && requestedRuntimeOverride === locked.harnessId;
if (!identityMatches || !harnessMatches) throw new Error(`Plugin "${pluginId}" may execute locked session "${sessionKey}" only with its exact persisted identity and harness.`);
return { ownerPluginId };
}
assertSessionIdentitiesOwned({
action: "run",
agentId: ownershipAgentId,
sessionFiles: [params.sessionFile],
sessionIds: [target?.sessionId ?? params.sessionId],
sessionKeys: [target?.sessionKey ?? params.sessionKey],
storePath: ownershipStorePath
});
if (!entry?.agentRuntimeOverride || params.agentHarnessRuntimeOverride !== void 0 || params.sessionPersistence === "detached" || params.modelRun || resolveSessionPinnedHarnessId(entry)) return {};
const cfg = params.config ?? currentSessionConfig();
const { provider } = resolveInitialEmbeddedRunModel({
config: cfg,
agentId: ownershipAgentId,
provider: params.provider,
model: params.model
});
return { agentHarnessRuntimeOverride: resolveSessionRuntimeOverrideForProvider({
provider,
entry,
cfg
}) };
};
const assertGatewaySessionRequestOwned = (method, params) => {
if (PLUGIN_GATEWAY_GLOBAL_SESSION_MUTATION_METHODS.has(method)) throw new Error(`Plugin "${pluginId}" cannot request global session mutation "${method}".`);
if (!PLUGIN_GATEWAY_SESSION_MUTATION_METHODS.has(method)) return;
const request = params ?? {};
if (method === "sessions.patchMany" && Array.isArray(request.targets)) {
for (const target of request.targets) {
if (!isRecord(target)) continue;
assertSessionIdentitiesOwned({
action: `request gateway method "${method}" for`,
agentId: target.agentId,
sessionKeys: [target.key]
});
}
return;
}
const sessionKeys = [
request.sessionKey,
request.key,
request.parentSessionKey
];
const sessionIds = [request.sessionId];
assertSessionIdentitiesOwned({
action: `request gateway method "${method}" for`,
agentId: request.agentId,
sessionIds,
sessionKeys
});
if (method === "sessions.abort" && !sessionKeys.some((value) => normalizeOptionalString(value)) && !sessionIds.some((value) => normalizeOptionalString(value))) throw new Error(`Plugin "${pluginId}" must provide a session key when requesting gateway method "${method}".`);
};
const assertStoreEntryOwned = (params) => {
if (params.entry.modelSelectionLocked === true) {
assertLockedSessionEntryOwned(params.sessionKey, params.entry, params.action);
return;
}
if (params.before?.modelSelectionLocked === true) {
assertLockedSessionEntryOwned(params.sessionKey, params.before, params.action);
return;
}
if (isAgentHarnessSessionKey(params.sessionKey) && !params.before) assertReservedSessionKeyOwned(params.sessionKey, params.action);
};
return {
assertOwnedHarness,
assertReservedSessionKeyOwned,
assertStoredSessionEntryOwned,
assertStoreEntryOwned,
resolveStoredSessionExecutionOwner,
prepareRunSessionExecution,
assertGatewaySessionRequestOwned,
assertSessionIdentitiesOwned
};
}
//#endregion
export { createPluginSessionOwnership };