openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
143 lines (142 loc) • 7.59 kB
JavaScript
import { t as isPromiseLike } from "./promise-like-D7-l5Fsp.mjs";
import "./account-id-DbRsdayc.mjs";
import { f as takeChannelParticipantInput, s as prepareHostChannelContextAdmissionEvidence, t as bindHostChannelContextAdmissionEvidence } from "./admission-evidence-D2DaumE3.mjs";
import { d as getCommandOwnerAuthority, l as bindCommandOwnerAuthority, u as captureCommandOwnerAssertion } from "./command-auth-CUBIvgy7.mjs";
import { t as captureChannelOperatorRunAuthority } from "./operator-run-authority-DfunQUuj.mjs";
//#region src/auto-reply/requester-profile.ts
const REQUESTER_PROFILE = Symbol("openclaw.requesterProfile");
function requesterBinding(context) {
return [
context.SenderId,
context.AccountId,
context.Provider,
context.Surface,
context.OriginatingChannel
];
}
var RequesterProfile = class {
#profile;
#binding;
constructor(context, profile) {
this.#profile = Object.freeze({ ...profile });
this.#binding = Object.freeze(requesterBinding(context));
Object.setPrototypeOf(this, null);
Object.freeze(this);
}
static read(value, context) {
if (typeof value !== "object" || value === null || !(#profile in value)) return;
const binding = requesterBinding(context);
return value.#binding.every((part, index) => part === binding[index]) && value.#profile.isCurrent() ? value.#profile : void 0;
}
};
/** Host-only, live identity fact: survives context copies, never JSON or plugin-authored fields. */
function bindRequesterProfile(context, profile) {
Object.assign(context, { [REQUESTER_PROFILE]: new RequesterProfile(context, profile) });
}
function getRequesterProfile(context) {
return RequesterProfile.read(Reflect.get(context, REQUESTER_PROFILE), context);
}
//#endregion
//#region src/sessions/session-participant-input.ts
const sessionParticipantInput = Symbol.for("openclaw.sessionParticipantInput");
/** Trusted ingress prepares once; context spreads carry the same consumed fact through retargeting. */
function prepareSessionParticipantInput(ctx, identity, promptedAt = Date.now()) {
(ctx[sessionParticipantInput] ??= []).push({
identity,
promptedAt,
recorded: false
});
}
function readSessionInputProfileId(ctx) {
const identity = ctx[sessionParticipantInput]?.find((input) => input.identity.type === "profile")?.identity;
return identity?.type === "profile" ? identity.id : void 0;
}
/** Only external turns may load the session's personal preferences; sender identity does not select them. */
function isSessionPersonalBootstrapTurn(ctx) {
return ctx.InternalTurnSource === void 0 && (!ctx.InputProvenance || ctx.InputProvenance.kind === "external_user") && Boolean(ctx[sessionParticipantInput]?.length);
}
/** An unqualified transport sender remains an observation, never a Gateway profile. */
function prepareChannelParticipantObservation(ctx) {
const channel = ctx.Provider ?? ctx.Surface;
if (ctx[sessionParticipantInput] || !ctx.SenderId || channel === "webchat" || ctx.InternalTurnSource !== void 0 || ctx.InputProvenance && ctx.InputProvenance.kind !== "external_user") return;
prepareSessionParticipantInput(ctx, {
type: "observation",
pluginId: channel ?? null,
accountId: ctx.AccountId ?? null,
senderKind: ctx.SenderIsBot === true ? "bot" : ctx.SenderIsBot === false ? "human" : "unknown",
id: ctx.SenderId
});
}
//#endregion
//#region src/channels/message-access/participant-input.ts
function bindChannelParticipantInput(params) {
if (!params.ingress || params.ingress === "unsupported") return;
const batch = (Array.isArray(params.ingress) ? params.ingress : [params.ingress]).map(takeChannelParticipantInput);
if (batch.at(-1)?.binding.messageId !== params.binding.messageId || !params.owner.isLive() || batch.some((input) => !input || input.owner !== params.owner || input.gatewayContext !== params.owner.resolveGatewayContext?.() || input.identity.pluginId !== params.channelId || input.binding.agentId !== params.binding.agentId || input.binding.sessionKey !== params.binding.sessionKey || input.binding.nativeChannelId !== params.binding.nativeChannelId || input.binding.inboundEventKind !== params.binding.inboundEventKind)) return;
for (const input of batch) if (input) prepareSessionParticipantInput(params.context, input.identity, input.promptedAt);
const principal = batch.at(-1)?.verifiedPrincipal;
const principalKey = principal && JSON.stringify(principal);
const gateway = params.owner.resolveGatewayContext?.();
if (!principal || !gateway || batch.some((input) => JSON.stringify(input?.verifiedPrincipal) !== principalKey)) return;
const requester = batch.at(-1)?.requesterProfile;
if (requester && params.context.SenderId === principal.senderId && (params.context.AccountId ?? "default") === principal.accountId && params.context.OriginatingChannel === principal.channelId && batch.every((input) => input?.requesterProfile?.id === requester.id && input.requesterProfile.isCurrent())) bindRequesterProfile(params.context, {
...requester,
isCurrent: () => params.owner.isLive() && params.owner.resolveGatewayContext?.() === gateway && batch.every((input) => input?.requesterProfile?.isCurrent())
});
const authority = batch.at(-1)?.commandOwnerAuthority;
if (!authority?.source || !authority.isCurrent(gateway.getRuntimeConfig())) return;
bindCommandOwnerAuthority(params.context, {
recoveryReference: authority.recoveryReference,
isCurrent: () => params.owner.isLive() && params.owner.resolveGatewayContext?.() === gateway && authority.isCurrent(gateway.getRuntimeConfig())
});
const assertCurrent = captureCommandOwnerAssertion(params.context);
const commandOwner = getCommandOwnerAuthority(params.context);
if (authority.operatorProfile && assertCurrent && commandOwner) bindCommandOwnerAuthority(params.context, {
...commandOwner,
operatorAuthority: captureChannelOperatorRunAuthority({
...authority.operatorProfile,
getRuntimeConfig: () => gateway.getRuntimeConfig(),
assertCurrent,
signal: authority.signal
})
});
}
//#endregion
//#region src/channels/inbound-event/host-context-builder.ts
/** Wrap the ordinary builder with the private bundled-channel evidence binding. */
function createHostChannelInboundEventContextBuilder(buildContext, owner) {
return (params) => {
const preparation = prepareHostChannelContextAdmissionEvidence({
owner,
channelId: params.channel,
accountId: params.accountId,
ingress: params.channelIngress,
rawPrincipalRef: params.sender.id,
contextParams: params
});
const result = buildContext(params);
const bindEvidence = (built) => {
if (owner?.channelId === params.channel && owner.isLive()) bindChannelParticipantInput({
context: built,
channelId: params.channel,
ingress: params.channelIngress,
owner,
binding: {
agentId: params.route.agentId,
sessionKey: params.route.dispatchSessionKey ?? params.route.routeSessionKey,
nativeChannelId: params.reply.nativeChannelId ?? params.conversation.nativeChannelId,
messageId: params.messageId,
inboundEventKind: params.message.inboundEventKind ?? "user_request"
}
});
bindHostChannelContextAdmissionEvidence({
context: built,
preparation
});
return built;
};
return isPromiseLike(result) ? result.then(bindEvidence) : bindEvidence(result);
};
}
//#endregion
export { readSessionInputProfileId as a, getRequesterProfile as c, prepareSessionParticipantInput as i, isSessionPersonalBootstrapTurn as n, sessionParticipantInput as o, prepareChannelParticipantObservation as r, bindRequesterProfile as s, createHostChannelInboundEventContextBuilder as t };