openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
310 lines (309 loc) • 13.4 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { m as ModelAccountConnectAuthorityError } from "./openclaw-state-worker-error-Bi-BnNrV.mjs";
import { n as normalizeAgentId } from "./agent-id-D1-Q6vR4.mjs";
import { r as isIncognitoSessionKey } from "./session-key-CmZ4o9-l.mjs";
import "./session-key-DY3MOX4N.mjs";
import { o as registerOpenClawAgentDatabaseAsyncResource } from "./openclaw-agent-db-resources-Cm-sH7kg.mjs";
import { h as resolveSessionAgentId } from "./agent-scope-bJOokm5r.mjs";
import { o as SESSION_READ_SCOPE } from "./operator-scopes-BO7_XQCg.mjs";
import { Y as validateChatMetadataParams } from "./src-D6kL8tMP.mjs";
import { G as readUserProfileAliasRevision } from "./user-profiles-internal-B_G1rF82.mjs";
import { t as ErrorCodes } from "./gateway-error-details-gTekQWj2.mjs";
import { d as errorShape } from "./error-codes-D0WRV4Bq.mjs";
import { c as readOperatorRolePolicyRevision } from "./operator-role-policy-BPiQfn95.mjs";
import { n as PreparedModelRuntimePublicationSupersededError } from "./prepared-model-runtime.errors-UvN8VA9R.mjs";
import { v as retainPreparedSessionEntryPredicate } from "./session-accessor.sqlite-entry-cache-publication-state-Ba-XKArI.mjs";
import { r as resolveRequestedSessionAgentId } from "./session-request-agent-BWnhhVvT.mjs";
import { i as resolveSessionStoreIdentity } from "./session-store-key-Dvf3CKvN.mjs";
import { l as readSessionWorkerPlacementAsync } from "./session-placement-lifecycle-D16TB6AJ.mjs";
import { t as SessionMutationAuthorizationChangedError } from "./session-mutation-authorization-error-Cdqn6r49.mjs";
import { t as chatMetadataSessionFields } from "./chat-metadata-contract-D2E5hwND.mjs";
import { u as withGatewaySessionEntry } from "./session-utils-store-qn-zT-Fk.mjs";
import { d as hiddenSessionNotFound, u as hasSessionReadAccessChanged } from "./session-sharing-policy-lVDdZ20H.mjs";
import "./session-sharing-BFM0s33P.mjs";
import { m as createSessionListEntryFilter } from "./session-sharing-target-read-CNtvN3Ha.mjs";
import { a as projectModelFastModeCatalog, n as prepareOperatorModelPresentation } from "./operator-model-presentation-QCZKBgRU.mjs";
import { t as assertValidParams } from "./validation-UW_NLjlV.mjs";
import { t as resolveAgentIdOrRespondError } from "./agent-id-shared-BvQwtL4x.mjs";
import { t as createPreparedReadHandler } from "./prepared-read-C_6yTKv-.mjs";
import { t as retainGatewaySessionEntryReadOnly } from "./session-utils-read-lifetime-Cu8XhhJZ.mjs";
import { a as readWorkerPlacementIdentity } from "./placement-projector-NloSZuIh.mjs";
import { t as preparePersonalModelAccountSelection } from "./users-model-account-access-CiXEeOi9.mjs";
import { t as prepareAuthenticatedProfile } from "./users-profile-access-BbaHON-U.mjs";
import { isDeepStrictEqual } from "node:util";
//#region src/gateway/server-methods/chat-metadata-session-read.ts
function sameMetadataEntry(previous, current) {
return previous && current ? !hasSessionReadAccessChanged(previous, current) && chatMetadataSessionFields.every((field) => isDeepStrictEqual(previous[field], current[field])) : previous === current;
}
async function prepareChatMetadataSessionRead(params) {
const changed = () => new PreparedModelRuntimePublicationSupersededError("Session changed while preparing its metadata. Retry the request.");
let active = true;
let retained;
const releases = [];
const preparations = /* @__PURE__ */ new Set();
const release = () => {
active = false;
for (const stop of releases.splice(0).toReversed()) stop();
};
const assertRefreshable = () => {
params.assertRequestCurrent();
if (!active || retained?.canRefresh() === false) throw changed();
};
const isCurrent = () => active && retained?.isCurrent() !== false;
const assertCurrent = () => {
assertRefreshable();
if (!isCurrent()) throw changed();
};
const options = {
agentId: params.agentId,
projection: []
};
try {
if (isIncognitoSessionKey(resolveSessionStoreIdentity(params).canonicalKey)) {
const selected = retainGatewaySessionEntryReadOnly(params.sessionKey, params.agentId, sameMetadataEntry, params.cfg);
releases.push(selected.release);
const assertNativeCurrent = () => {
assertRefreshable();
if (!selected.isCurrentAtResponse()) throw changed();
};
assertNativeCurrent();
return {
selected,
isCurrent: selected.isCurrent,
assertCurrent: assertNativeCurrent,
beforeRequest: assertNativeCurrent,
release,
async withCurrent(consume) {
assertNativeCurrent();
return await consume();
}
};
}
const selected = await withGatewaySessionEntry(params.sessionKey, options, (session) => {
assertCurrent();
const sources = new Map([
{
agentId: session.readSource?.agentId ?? session.agentId,
path: session.storePath
},
...session.readSource ? [session.readSource] : [],
...session.capturedReadSources ?? []
].map((source) => [source.path, source]));
for (const source of sources.values()) releases.push(registerOpenClawAgentDatabaseAsyncResource({
...source,
revoke: release,
close: async () => {
release();
await Promise.allSettled(preparations);
}
}));
if (session.capturedReadSource) {
if (typeof session.capturedReadSource.databaseIdentity !== "string") throw changed();
retained = retainPreparedSessionEntryPredicate({
databaseIdentity: `file:${session.capturedReadSource.databaseIdentity}`,
sessionKey: session.legacyKey ?? session.canonicalKey,
entry: session.entry,
matches: sameMetadataEntry
});
releases.push(retained.release);
}
return session;
}, params.cfg, assertCurrent);
assertCurrent();
const matchesSelected = (current) => current.agentId === selected.agentId && current.canonicalKey === selected.canonicalKey && current.legacyKey === selected.legacyKey && current.storePath === selected.storePath && isDeepStrictEqual(current.readSource, selected.readSource) && isDeepStrictEqual(current.capturedReadSource, selected.capturedReadSource) && isDeepStrictEqual(current.capturedReadSources, selected.capturedReadSources) && sameMetadataEntry(selected.entry, current.entry);
let requestRead;
return {
selected,
isCurrent,
assertCurrent,
beforeRequest: () => {
assertCurrent();
if (!requestRead) {
requestRead = retainGatewaySessionEntryReadOnly(params.sessionKey, params.agentId, sameMetadataEntry, params.cfg);
releases.push(requestRead.release);
}
if (!matchesSelected(requestRead) || !requestRead.isCurrentAtResponse()) throw changed();
},
release,
async withCurrent(consume) {
assertRefreshable();
const revision = retained?.captureRevision();
let preparation;
let failure;
const reading = withGatewaySessionEntry(params.sessionKey, options, (current) => {
assertRefreshable();
if (!matchesSelected(current) || retained && !retained.acknowledge(current.entry, revision)) throw changed();
assertCurrent();
preparation = (async () => await consume())();
const pending = preparation;
preparations.add(pending);
const settled = () => preparations.delete(pending);
pending.then(settled, (error) => {
failure ??= { error };
settled();
});
return { value: pending };
}, params.cfg, assertRefreshable);
try {
return await (await reading).value;
} catch (error) {
failure ??= { error };
release();
await Promise.allSettled(preparation ? [preparation] : []);
throw failure.error;
}
}
};
} catch (error) {
release();
throw error;
}
}
//#endregion
//#region src/gateway/server-methods/chat-metadata-handler.ts
/** Resolve saved-session grants or capture a new draft's current human authority. */
async function resolveChatMetadataReadParams(options, params, draftAccountSelection) {
const { respond, context, client, signal } = options;
const cfg = context.getRuntimeConfig();
draftAccountSelection?.assertCurrent();
const requester = await prepareAuthenticatedProfile(options);
requester.assertCurrent();
const roleRevision = readOperatorRolePolicyRevision();
const aliasRevision = readUserProfileAliasRevision();
const profileInput = client?.authenticatedUserProfile?.profileId;
const userInput = client?.authenticatedUserId;
const isRequestCurrent = () => !signal?.aborted && readOperatorRolePolicyRevision() === roleRevision && readUserProfileAliasRevision() === aliasRevision && client?.authenticatedUserProfile?.profileId === profileInput && client?.authenticatedUserId === userInput;
const assertRequestCurrent = () => {
if (!isRequestCurrent() || context.getRuntimeConfig() !== cfg) throw new PreparedModelRuntimePublicationSupersededError("Chat metadata access changed while preparing its metadata. Retry the request.");
requester.assertCurrent();
draftAccountSelection?.assertCurrent();
};
if (params.sessionKey) {
const sessionKey = params.sessionKey;
const requested = resolveRequestedSessionAgentId(cfg, params.sessionKey, normalizeOptionalString(params.agentId));
if (!requested.ok) {
respond(false, void 0, requested.error);
return;
}
const requesterProfileId = requester.profileId;
const read = await prepareChatMetadataSessionRead({
cfg,
sessionKey,
agentId: requested.agentId,
assertRequestCurrent
});
const session = read.selected;
const isCurrent = () => isRequestCurrent() && read.isCurrent();
const assertVisible = () => {
const visible = createSessionListEntryFilter({
client,
cfg: (context.getCommittedRuntimeConfig ?? context.getRuntimeConfig)()
});
if (session.entry && visible?.(session.legacyKey ?? session.canonicalKey, session.entry) === false) throw new SessionMutationAuthorizationChangedError(hiddenSessionNotFound(sessionKey));
};
try {
assertVisible();
const sessionId = session.entry?.sessionId;
const placement = params.includeModels === false ? void 0 : await readSessionWorkerPlacementAsync({
context,
sessionId
});
const workerInference = placement ? readWorkerPlacementIdentity(placement, context.workerEnvironmentService)?.inference : void 0;
assertVisible();
assertRequestCurrent();
read.assertCurrent();
return {
agentId: resolveSessionAgentId({
sessionKey: params.sessionKey,
config: session.cfg,
agentId: requested.agentId
}),
sessionKey: session.canonicalKey,
storePath: session.readSource?.path ?? session.storePath,
sessionEntry: session.entry,
...workerInference ? { workerInference } : {},
isCurrent,
assertCurrent: () => {
assertVisible();
assertRequestCurrent();
read.assertCurrent();
},
withCurrent: (consume) => read.withCurrent(consume),
beforeRequest: () => {
assertVisible();
assertRequestCurrent();
read.beforeRequest();
},
release: read.release,
requesterProfileId
};
} catch (error) {
read.release();
throw error;
}
}
const resolved = resolveAgentIdOrRespondError({
rawAgentId: params.agentId?.trim() ? normalizeAgentId(params.agentId) : void 0,
respond,
cfg
});
if (!resolved) return;
assertRequestCurrent();
return {
agentId: resolved.agentId,
requesterProfileId: draftAccountSelection?.owner ?? requester.profileId,
isCurrent: isRequestCurrent,
assertCurrent: assertRequestCurrent,
...draftAccountSelection ? { draftAccountSelection } : {}
};
}
const handleChatMetadataRequest = createPreparedReadHandler(async (options) => {
const { params, respond: respondToCaller, context, client } = options;
if (!assertValidParams(params, validateChatMetadataParams, "chat.metadata", respondToCaller)) return;
let scope;
try {
scope = await resolveChatMetadataReadParams(options, params, !params.sessionKey && params.authProfileId ? await preparePersonalModelAccountSelection(options, params.authProfileId, SESSION_READ_SCOPE) : void 0);
if (!scope) return;
if (params.includeModels === false) {
scope.includeModels = false;
scope.ifRevision = params.ifRevision;
}
const readScope = scope;
const assertCurrent = () => {
readScope.draftAccountSelection?.assertCurrent();
readScope.assertCurrent?.();
};
assertCurrent();
return {
assertCurrent,
release: readScope.release,
run: async (respond) => {
const metadata = await context.readChatMetadata(readScope);
const publish = () => {
assertCurrent();
const cfg = context.getRuntimeConfig();
const policy = metadata.models && prepareOperatorModelPresentation({
cfg,
policyConfig: context.getCommittedRuntimeConfig?.() ?? cfg,
client
})?.forAgent(readScope.agentId, metadata.models);
respond(true, projectModelFastModeCatalog(policy ? policy.metadata(metadata) : metadata, client));
};
if (readScope.withCurrent) await readScope.withCurrent(publish);
else publish();
}
};
} catch (error) {
scope?.release?.();
throw error;
}
}, (error, { respond }) => {
if (error instanceof SessionMutationAuthorizationChangedError) {
respond(false, void 0, error.error);
return;
}
if (!(error instanceof ModelAccountConnectAuthorityError)) throw error;
respond(false, void 0, errorShape(ErrorCodes.FORBIDDEN, error.message));
});
//#endregion
export { resolveChatMetadataReadParams as n, handleChatMetadataRequest as t };