UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,793 lines 65.7 kB
import "./src-vebZIeLe.js";
import { t as expectDefined } from "./expect-CyE8FADM.js";
import { d as normalizeStringEntries, y as uniqueStrings } from "./string-normalization-DsCfAx8q.js";
import "./session-key-BnWWjqNc.js";
import { n as normalizeAccountId } from "./account-id-CETVCrTz.js";
import { t as resolveAccountEntry } from "./account-lookup-CvFhSs3G.js";
import { f as recordChannelIngressResolution, p as readChannelIngressHostOwner } from "./admission-evidence-BW_2Woj4.js";
import { a as prepareChannelParticipantInput } from "./ingress-drain-DcmwOHP5.js";
import { a as parseAccessGroupAllowFromEntry } from "./allow-from-PKDuk4I7.js";
import { t as resolveChannelIngressEffectiveAllowFromLists } from "./effective-allow-from-BBUHJIOa.js";
import { t as readChannelIngressStoreAllowFromForDmPolicy } from "./store-allow-from-DYAkflRM.js";
import { t as resolveCommandAuthorizedFromAuthorizers } from "./command-gating-65fgTdwb.js";
import { r as resolveInboundMentionDecision, t as allowedImplicitMentionKindsFromConfig } from "./mention-gating-Cqy7URJJ.js";
import { n as createChannelIngressMonitor } from "./ingress-monitor--JQILMnk.js";
//#region src/channels/message-access/identifier-authentication.ts
const IDENTIFIER_AUTHENTICATION_RANK = {
	verified: 3,
	asserted: 2,
	unverified: 1,
	mutable: 0
};
function meetsIdentifierAuthentication(actual, minimum) {
	return IDENTIFIER_AUTHENTICATION_RANK[actual] >= IDENTIFIER_AUTHENTICATION_RANK[minimum];
}
/** Entry and subject claims combine by taking the weaker exact-pair claim. */
function weakestIdentifierAuthentication(entry, subject) {
	return IDENTIFIER_AUTHENTICATION_RANK[entry] <= IDENTIFIER_AUTHENTICATION_RANK[subject] ? entry : subject;
}
function identifierAuthenticationFrom(params) {
	return params.authentication ?? (params.dangerous ? "mutable" : "asserted");
}
function minimumIdentifierAuthenticationFrom(params) {
	return params.minIdentifierAuthentication ?? (params.mutableIdentifierMatching === "enabled" ? "mutable" : "asserted");
}
//#endregion
//#region src/channels/message-access/allowlist.ts
/**
* Channel ingress allowlist diagnostics.
*
* Merges allowlists, applies identifier authentication policy, and redacts access-graph facts.
*/
/**
* Returns the first access-group related failure reason for an allowlist.
*/
function allowlistFailureReason(allowlist) {
	if (allowlist.accessGroups.failed.length > 0) return "access_group_failed";
	if (allowlist.accessGroups.unsupported.length > 0) return "access_group_unsupported";
	if (allowlist.accessGroups.missing.length > 0) return "access_group_missing";
	return null;
}
/**
* Projects an allowlist into redacted diagnostics safe for ingress access graphs.
*/
function redactedAllowlistDiagnostics(allowlist, reasonCode) {
	return {
		configured: allowlist.hasConfiguredEntries,
		matched: allowlist.match.matched,
		reasonCode,
		matchedEntryIds: allowlist.matchedEntryIds,
		invalidEntryCount: allowlist.invalidEntries.length,
		disabledEntryCount: allowlist.disabledEntries.length,
		accessGroups: allowlist.accessGroups
	};
}
function mergeResolvedAllowlists(allowlists) {
	const scopedAllowlists = [];
	for (const [index, allowlist] of allowlists.entries()) {
		const prefix = `source-${index + 1}:`;
		const normalizedEntries = [];
		for (const entry of allowlist.normalizedEntries) normalizedEntries.push({
			...entry,
			opaqueEntryId: `${prefix}${entry.opaqueEntryId}`
		});
		const matchedPairs = [];
		for (const pair of allowlist.match.matchedPairs ?? []) matchedPairs.push({
			...pair,
			opaqueEntryId: `${prefix}${pair.opaqueEntryId}`
		});
		const matchedEntryIds = allowlist.matchedEntryIds.map((id) => `${prefix}${id}`);
		scopedAllowlists.push({
			...allowlist,
			normalizedEntries,
			matchedEntryIds,
			match: {
				...allowlist.match,
				matchedEntryIds,
				...matchedPairs.length > 0 ? { matchedPairs } : {}
			}
		});
	}
	const matches = scopedAllowlists.map((allowlist) => allowlist.match);
	const matchedEntryIds = uniqueStrings(scopedAllowlists.flatMap((allowlist) => allowlist.matchedEntryIds));
	const matchedPairs = scopedAllowlists.flatMap((allowlist) => allowlist.match.matchedPairs ?? []);
	return {
		rawEntryCount: scopedAllowlists.reduce((sum, allowlist) => sum + allowlist.rawEntryCount, 0),
		normalizedEntries: scopedAllowlists.flatMap((allowlist) => allowlist.normalizedEntries),
		invalidEntries: scopedAllowlists.flatMap((allowlist) => allowlist.invalidEntries),
		disabledEntries: scopedAllowlists.flatMap((allowlist) => allowlist.disabledEntries),
		matchedEntryIds,
		hasConfiguredEntries: scopedAllowlists.some((allowlist) => allowlist.hasConfiguredEntries),
		hasMatchableEntries: scopedAllowlists.some((allowlist) => allowlist.hasMatchableEntries),
		hasWildcard: scopedAllowlists.some((allowlist) => allowlist.hasWildcard),
		accessGroups: {
			referenced: uniqueStrings(scopedAllowlists.flatMap((allowlist) => allowlist.accessGroups.referenced)),
			matched: uniqueStrings(scopedAllowlists.flatMap((allowlist) => allowlist.accessGroups.matched)),
			missing: uniqueStrings(scopedAllowlists.flatMap((allowlist) => allowlist.accessGroups.missing)),
			unsupported: uniqueStrings(scopedAllowlists.flatMap((allowlist) => allowlist.accessGroups.unsupported)),
			failed: uniqueStrings(scopedAllowlists.flatMap((allowlist) => allowlist.accessGroups.failed))
		},
		match: {
			matched: matches.some((match) => match.matched) || matchedEntryIds.length > 0,
			matchedEntryIds,
			...matchedPairs.length > 0 ? { matchedPairs } : {}
		}
	};
}
/**
* Applies identifier authentication to exact matched entry/subject pairs.
*/
function applyIdentifierAuthenticationPolicy(allowlist, policy) {
	const minimum = minimumIdentifierAuthenticationFrom(policy);
	const pairsByEntry = /* @__PURE__ */ new Map();
	for (const pair of allowlist.match.matchedPairs ?? []) {
		const pairs = pairsByEntry.get(pair.opaqueEntryId) ?? [];
		pairs.push(pair);
		pairsByEntry.set(pair.opaqueEntryId, pairs);
	}
	const rejectedEntryIds = /* @__PURE__ */ new Set();
	for (const entry of allowlist.normalizedEntries) {
		const pairStrengths = pairsByEntry.get(entry.opaqueEntryId)?.map((pair) => weakestIdentifierAuthentication(entry.authentication, pair.subjectAuthentication));
		if (!(pairStrengths && pairStrengths.length > 0 ? pairStrengths.some((strength) => meetsIdentifierAuthentication(strength, minimum)) : meetsIdentifierAuthentication(entry.authentication, minimum))) rejectedEntryIds.add(entry.opaqueEntryId);
	}
	const matchedEntryIds = allowlist.matchedEntryIds.filter((id) => !rejectedEntryIds.has(id));
	const matchedPairs = allowlist.match.matchedPairs?.filter((pair) => !rejectedEntryIds.has(pair.opaqueEntryId));
	const disabledEntries = [...allowlist.disabledEntries, ...allowlist.normalizedEntries.filter((entry) => rejectedEntryIds.has(entry.opaqueEntryId)).map((entry) => ({
		opaqueEntryId: entry.opaqueEntryId,
		reasonCode: entry.authentication === "mutable" ? "mutable_identifier_disabled" : "identifier_authentication_too_weak"
	}))];
	const affectedMatch = matchedEntryIds.length !== allowlist.matchedEntryIds.length;
	return {
		...allowlist,
		disabledEntries,
		matchedEntryIds,
		hasMatchableEntries: allowlist.normalizedEntries.some((entry) => !rejectedEntryIds.has(entry.opaqueEntryId)),
		match: {
			matched: matchedEntryIds.length > 0,
			matchedEntryIds,
			...matchedPairs ? { matchedPairs } : {}
		},
		authentication: {
			evaluated: policy.minIdentifierAuthentication !== void 0 || policy.mutableIdentifierMatching !== void 0 || Boolean(allowlist.match.matchedPairs?.length),
			threshold: minimum,
			affectedMatch,
			rejectedEntryIds: [...rejectedEntryIds]
		}
	};
}
/**
* Resolves the sender allowlist used for group/channel ingress after route overrides.
*/
function effectiveGroupSenderAllowlist(params) {
	let effective = params.policy.groupAllowFromFallbackToAllowFrom && !params.state.allowlists.group.hasConfiguredEntries ? params.state.allowlists.dm : params.state.allowlists.group;
	for (const route of params.state.routeFacts) {
		if (route.gate !== "matched" || !route.senderAllowlist) continue;
		if (route.senderPolicy === "inherit") {
			effective = mergeResolvedAllowlists([effective, route.senderAllowlist]);
			continue;
		}
		effective = route.senderAllowlist;
	}
	return applyIdentifierAuthenticationPolicy(effective, params.policy);
}
//#endregion
//#region src/channels/message-access/sender-gates.ts
/**
* Channel ingress sender gate helpers.
*
* Evaluates DM and group sender policies against normalized allowlists.
*/
function senderGate(params) {
	return {
		id: params.id,
		phase: "sender",
		kind: params.kind,
		effect: params.effect,
		allowed: params.allowed,
		reasonCode: params.reasonCode,
		match: params.match,
		sender: { policy: params.policy },
		allowlist: redactedAllowlistDiagnostics(params.allowlistSource, params.reasonCode),
		...params.allowlistSource.authentication ? { identifierAuthentication: {
			evaluated: params.allowlistSource.authentication.evaluated,
			affectedMatch: params.allowlistSource.authentication.affectedMatch
		} } : {}
	};
}
/**
* Evaluates direct-message sender policy against DM and pairing-store allowlists.
*/
function senderGateForDirect(params) {
	const dm = applyIdentifierAuthenticationPolicy(params.state.allowlists.dm, params.policy);
	const pairingStore = applyIdentifierAuthenticationPolicy(params.state.allowlists.pairingStore, params.policy);
	const base = {
		policy: params.policy.dmPolicy,
		allowlistSource: dm,
		match: dm.match
	};
	const allow = (reasonCode) => senderGate({
		id: "sender:dm",
		kind: "dmSender",
		...base,
		effect: "allow",
		allowed: true,
		reasonCode
	});
	const block = (reasonCode) => senderGate({
		id: "sender:dm",
		kind: "dmSender",
		...base,
		effect: "block-dispatch",
		allowed: false,
		reasonCode
	});
	if (params.policy.dmPolicy === "disabled") return block("dm_policy_disabled");
	if (params.policy.dmPolicy === "open") {
		if (dm.hasWildcard) return allow("dm_policy_open");
		if (dm.match.matched) return allow("dm_policy_allowlisted");
		return block("dm_policy_not_allowlisted");
	}
	if (dm.match.matched) return allow("dm_policy_allowlisted");
	if (params.policy.dmPolicy === "pairing" && pairingStore.match.matched) return senderGate({
		id: "sender:dm",
		kind: "dmSender",
		effect: "allow",
		allowed: true,
		reasonCode: "dm_policy_allowlisted",
		match: pairingStore.match,
		policy: params.policy.dmPolicy,
		allowlistSource: pairingStore
	});
	if (params.policy.dmPolicy === "pairing" && params.state.event.mayPair) return block("dm_policy_pairing_required");
	return block(params.policy.dmPolicy === "pairing" ? "event_pairing_not_allowed" : allowlistFailureReason(dm) ?? "dm_policy_not_allowlisted");
}
/**
* Evaluates group/channel sender policy after route sender allowlist overrides are applied.
*/
function senderGateForGroup(params) {
	const group = effectiveGroupSenderAllowlist(params);
	const base = {
		policy: params.policy.groupPolicy,
		allowlistSource: group,
		match: group.match
	};
	const allow = (reasonCode) => senderGate({
		id: "sender:group",
		kind: "groupSender",
		...base,
		effect: "allow",
		allowed: true,
		reasonCode
	});
	const block = (reasonCode) => senderGate({
		id: "sender:group",
		kind: "groupSender",
		...base,
		effect: "block-dispatch",
		allowed: false,
		reasonCode
	});
	if (params.policy.groupPolicy === "disabled") return block("group_policy_disabled");
	if (params.policy.groupPolicy === "open") return allow("group_policy_open");
	if (!group.hasConfiguredEntries) return block("group_policy_empty_allowlist");
	if (group.match.matched) return allow("group_policy_allowed");
	return block(allowlistFailureReason(group) ?? "group_policy_not_allowlisted");
}
/**
* Applies event auth mode to sender gates for non-message callbacks.
*/
function applyEventAuthModeToSenderGate(params) {
	if (params.state.event.authMode === "inbound" || params.senderGate.allowed) return params.senderGate;
	const reasonCode = "sender_not_required";
	return {
		...params.senderGate,
		effect: "ignore",
		allowed: true,
		reasonCode,
		allowlist: params.senderGate.allowlist ? {
			...params.senderGate.allowlist,
			reasonCode
		} : void 0
	};
}
//#endregion
//#region src/channels/message-access/decision.ts
/**
* Channel ingress decision graph builder.
*
* Evaluates route, sender, command, and mention gates into one admission decision.
*/
function decisiveDecision(params) {
	return {
		admission: params.admission,
		decision: params.decision,
		decisiveGateId: params.gate.id,
		reasonCode: params.gate.reasonCode,
		graph: { gates: params.gates }
	};
}
function routeGates(state) {
	return state.routeFacts.map((route) => ({
		id: route.id,
		phase: "route",
		kind: route.kind,
		effect: route.effect,
		allowed: route.effect !== "block-dispatch",
		reasonCode: route.effect === "block-dispatch" ? "route_blocked" : "allowed",
		match: route.match
	}));
}
function routeSenderEmptyGate(state) {
	const route = state.routeFacts.find((fact) => fact.senderPolicy === "deny-when-empty" && fact.gate === "matched" && fact.senderAllowlist?.hasConfiguredEntries !== true);
	if (!route) return null;
	const reasonCode = "route_sender_empty";
	return {
		id: `${route.id}:sender`,
		phase: "route",
		kind: "routeSender",
		effect: "block-dispatch",
		allowed: false,
		reasonCode,
		match: route.match,
		allowlist: route.senderAllowlist ? redactedAllowlistDiagnostics(route.senderAllowlist, reasonCode) : void 0
	};
}
function commandGate(params) {
	const command = params.policy.command;
	if (!command) return {
		id: "command",
		phase: "command",
		kind: "command",
		effect: "allow",
		allowed: true,
		reasonCode: "command_authorized"
	};
	const useAccessGroups = command.useAccessGroups ?? true;
	const owner = applyIdentifierAuthenticationPolicy(params.state.allowlists.commandOwner, params.policy);
	const group = applyIdentifierAuthenticationPolicy(params.state.allowlists.commandGroup, params.policy);
	const authorized = resolveCommandAuthorizedFromAuthorizers({
		useAccessGroups,
		modeWhenAccessGroupsOff: command.modeWhenAccessGroupsOff,
		authorizers: [{
			configured: owner.hasConfiguredEntries,
			allowed: owner.match.matched
		}, {
			configured: group.hasConfiguredEntries,
			allowed: group.match.matched
		}]
	});
	const shouldBlock = command.allowTextCommands && command.hasControlCommand && !authorized;
	return {
		id: "command",
		phase: "command",
		kind: "command",
		effect: shouldBlock ? "block-command" : "allow",
		allowed: authorized,
		reasonCode: shouldBlock ? "control_command_unauthorized" : "command_authorized",
		match: mergeCommandMatch(owner.match, group.match),
		identifierAuthentication: {
			evaluated: Boolean(owner.authentication?.evaluated || group.authentication?.evaluated),
			affectedMatch: Boolean(owner.authentication?.affectedMatch || group.authentication?.affectedMatch)
		},
		command: {
			useAccessGroups,
			allowTextCommands: command.allowTextCommands,
			modeWhenAccessGroupsOff: command.modeWhenAccessGroupsOff,
			shouldBlockControlCommand: shouldBlock
		}
	};
}
function mergeCommandMatch(owner, group) {
	const matchedEntryIds = uniqueStrings([...owner.matchedEntryIds, ...group.matchedEntryIds]);
	return {
		matched: owner.matched || group.matched || matchedEntryIds.length > 0,
		matchedEntryIds
	};
}
function eventGate(params) {
	const authMode = params.state.event.authMode;
	const event = params.state.event;
	const eventResult = (allowed, reasonCode) => ({
		id: "event",
		phase: "event",
		kind: "event",
		effect: allowed ? "allow" : "block-dispatch",
		allowed,
		reasonCode,
		event
	});
	if (authMode === "none" || authMode === "route-only") return eventResult(true, "event_authorized");
	if (authMode === "command") return eventResult(params.commandGate.allowed, params.commandGate.allowed ? "event_authorized" : "event_unauthorized");
	if (authMode === "origin-subject") {
		if (!params.state.event.hasOriginSubject) return eventResult(false, "origin_subject_missing");
		const matched = params.state.event.originSubjectMatched && meetsIdentifierAuthentication(params.state.event.originSubjectAuthentication ?? "asserted", minimumIdentifierAuthenticationFrom(params.policy));
		return {
			...eventResult(matched, matched ? "event_authorized" : "origin_subject_not_matched"),
			identifierAuthentication: {
				evaluated: params.policy.minIdentifierAuthentication !== void 0 || params.policy.mutableIdentifierMatching !== void 0 || params.state.event.originSubjectAuthentication !== void 0,
				affectedMatch: params.state.event.originSubjectMatched && !matched
			}
		};
	}
	return eventResult(params.senderGate.allowed, params.senderGate.allowed ? "event_authorized" : "event_unauthorized");
}
function activationMetadata(params) {
	const mentionFacts = params.mentionFacts;
	const allowedImplicitMentionKinds = resolveAllowedImplicitMentionKinds(params.activation);
	return {
		hasMentionFacts: mentionFacts != null,
		requireMention: params.activation?.requireMention ?? false,
		allowTextCommands: params.activation?.allowTextCommands ?? false,
		...allowedImplicitMentionKinds !== void 0 ? { allowedImplicitMentionKinds } : {},
		...params.activation?.order ? { order: params.activation.order } : {},
		shouldSkip: params.shouldSkip,
		...mentionFacts?.canDetectMention !== void 0 ? { canDetectMention: mentionFacts.canDetectMention } : {},
		...mentionFacts?.wasMentioned !== void 0 ? { wasMentioned: mentionFacts.wasMentioned } : {},
		...mentionFacts?.hasAnyMention !== void 0 ? { hasAnyMention: mentionFacts.hasAnyMention } : {},
		...mentionFacts?.implicitMentionKinds !== void 0 ? { implicitMentionKinds: mentionFacts.implicitMentionKinds } : {},
		...params.effectiveWasMentioned !== void 0 ? { effectiveWasMentioned: params.effectiveWasMentioned } : {},
		...params.shouldBypassMention !== void 0 ? { shouldBypassMention: params.shouldBypassMention } : {}
	};
}
function resolveAllowedImplicitMentionKinds(activation) {
	return activation?.allowedImplicitMentionKinds ?? (activation?.implicitMentions ? allowedImplicitMentionKindsFromConfig(activation.implicitMentions) : void 0);
}
function activationGate(params) {
	const activation = params.policy.activation;
	const mentionFacts = params.state.mentionFacts;
	const allowedImplicitMentionKinds = resolveAllowedImplicitMentionKinds(activation);
	const activationResult = (input) => ({
		id: "activation",
		phase: "activation",
		kind: "mention",
		effect: input.shouldSkip ? "skip" : "allow",
		allowed: !input.shouldSkip,
		reasonCode: input.shouldSkip ? "activation_skipped" : "activation_allowed",
		activation: activationMetadata({
			activation,
			mentionFacts,
			shouldSkip: input.shouldSkip,
			effectiveWasMentioned: input.effectiveWasMentioned,
			shouldBypassMention: input.shouldBypassMention
		})
	});
	if (!activation || !mentionFacts) return activationResult({
		shouldSkip: false,
		effectiveWasMentioned: mentionFacts && (mentionFacts.wasMentioned || Boolean(mentionFacts.implicitMentionKinds?.length))
	});
	const result = resolveInboundMentionDecision({
		facts: mentionFacts,
		policy: {
			isGroup: params.state.conversationKind !== "direct",
			requireMention: activation.requireMention,
			allowedImplicitMentionKinds,
			allowTextCommands: activation.allowTextCommands,
			hasControlCommand: params.policy.command?.hasControlCommand ?? false,
			commandAuthorized: params.commandGate.allowed
		}
	});
	return activationResult({
		shouldSkip: result.shouldSkip,
		effectiveWasMentioned: result.effectiveWasMentioned,
		shouldBypassMention: result.shouldBypassMention
	});
}
function decideChannelIngress(state, policy) {
	const gates = routeGates(state);
	const emptyRouteSenderGate = routeSenderEmptyGate(state);
	if (emptyRouteSenderGate) gates.push(emptyRouteSenderGate);
	const routeBlock = gates.find((entry) => entry.effect === "block-dispatch");
	if (routeBlock) return decisiveDecision({
		admission: "drop",
		decision: "block",
		gate: routeBlock,
		gates
	});
	const activationBeforeSender = policy.activation?.order === "before-sender" && !policy.activation.allowTextCommands ? activationGate({
		state,
		policy,
		commandGate: commandGate({
			state,
			policy: {
				...policy,
				command: void 0
			}
		})
	}) : null;
	if (activationBeforeSender) {
		gates.push(activationBeforeSender);
		if (activationBeforeSender.effect === "skip") return decisiveDecision({
			admission: "skip",
			decision: "allow",
			gate: activationBeforeSender,
			gates
		});
	}
	const eventModeSender = applyEventAuthModeToSenderGate({
		state,
		senderGate: state.conversationKind === "direct" ? senderGateForDirect({
			state,
			policy
		}) : senderGateForGroup({
			state,
			policy
		})
	});
	gates.push(eventModeSender);
	if (!eventModeSender.allowed) return decisiveDecision({
		admission: eventModeSender.reasonCode === "dm_policy_pairing_required" ? "pairing-required" : "drop",
		decision: eventModeSender.reasonCode === "dm_policy_pairing_required" ? "pairing" : "block",
		gate: eventModeSender,
		gates
	});
	const command = commandGate({
		state,
		policy
	});
	gates.push(command);
	if (command.effect === "block-command") return decisiveDecision({
		admission: "drop",
		decision: "block",
		gate: command,
		gates
	});
	const event = eventGate({
		state,
		policy,
		senderGate: eventModeSender,
		commandGate: command
	});
	gates.push(event);
	if (!event.allowed) return decisiveDecision({
		admission: "drop",
		decision: "block",
		gate: event,
		gates
	});
	const activation = activationBeforeSender ?? activationGate({
		state,
		policy,
		commandGate: command
	});
	if (!activationBeforeSender) gates.push(activation);
	if (activation.effect === "skip") return decisiveDecision({
		admission: "skip",
		decision: "allow",
		gate: activation,
		gates
	});
	if (activation.effect === "observe") return decisiveDecision({
		admission: "observe",
		decision: "allow",
		gate: activation,
		gates
	});
	return decisiveDecision({
		admission: "dispatch",
		decision: "allow",
		gate: activation,
		gates
	});
}
//#endregion
//#region src/channels/message-access/runtime-access-groups.ts
/**
* Runtime access-group resolution for channel ingress.
*
* Preserves symbolic access-group entries until dynamic membership facts are available.
*/
function accessGroupNames(entries) {
	return uniqueStrings(entries.map((entry) => parseAccessGroupAllowFromEntry(String(entry))).filter((entry) => entry != null));
}
/**
* Lists every access-group name referenced by grouped allowFrom entry arrays.
*/
function allReferencedAccessGroupNames(entries) {
	return uniqueStrings(entries.flatMap((entryGroup) => accessGroupNames(entryGroup)));
}
/**
* Normalizes direct sender entries while preserving access-group references for runtime lookup.
*/
async function normalizeEffectiveEntries(params) {
	const rawEntries = normalizeStringEntries(params.entries);
	const accessGroupEntries = rawEntries.filter((entry) => parseAccessGroupAllowFromEntry(entry) != null);
	const directEntries = rawEntries.filter((entry) => parseAccessGroupAllowFromEntry(entry) == null);
	if (directEntries.length === 0) return accessGroupEntries;
	const normalized = await params.adapter.normalizeEntries({
		entries: directEntries,
		context: params.context,
		accountId: params.accountId
	});
	return uniqueStrings([...accessGroupEntries, ...normalized.matchable.map((entry) => entry.value)]);
}
/**
* Resolves dynamic access-group membership facts for referenced runtime access groups.
*/
async function resolveRuntimeAccessGroupMembershipFacts(params) {
	if (!params.input.resolveAccessGroupMembership || params.names.length === 0) return [];
	const facts = [];
	for (const name of params.names) {
		const group = params.input.accessGroups?.[name];
		if (!group || group.type === "message.senders") continue;
		try {
			const matched = await params.input.resolveAccessGroupMembership({
				name,
				group,
				channelId: params.channelId,
				accountId: params.input.accountId,
				subject: params.input.subject
			});
			facts.push(matched ? {
				kind: "matched",
				groupName: name,
				source: "dynamic",
				matchedEntryIds: [`access-group:${name}`]
			} : {
				kind: "not-matched",
				groupName: name,
				source: "dynamic"
			});
		} catch {
			facts.push({
				kind: "failed",
				groupName: name,
				source: "dynamic",
				reasonCode: "access_group_failed",
				diagnosticId: `access-group:${name}`
			});
		}
	}
	return facts;
}
//#endregion
//#region src/channels/message-access/runtime-identity.ts
/** Build an identity descriptor for channels with one stable id and optional aliases. */
function defineStableChannelIngressIdentity(params = {}) {
	const { entryIdPrefix, resolveEntryId, aliases, isWildcardEntry, matchEntry, resolveParticipant, ...primary } = params;
	return {
		primary,
		aliases,
		isWildcardEntry,
		matchEntry,
		resolveParticipant,
		resolveEntryId: resolveEntryId ?? (entryIdPrefix ? ({ entryIndex }) => `${entryIdPrefix}-${entryIndex + 1}` : void 0)
	};
}
/** Classify configured entries without needing a sender or granting admission. */
function identityEntryAuthenticationClassifier(identity) {
	const descriptor = "primary" in identity ? identity : defineStableChannelIngressIdentity(identity);
	const fields = identityFields(descriptor);
	const isWildcardEntry = descriptor.isWildcardEntry ?? ((value) => value === "*");
	return (raw) => {
		if (isWildcardEntry(raw)) return;
		let strongest;
		for (const field of fields) {
			if (!normalizeFieldValue(field, raw, "entry")) continue;
			const authentication = fieldAuthentication(field, raw, fieldDangerous(field, raw));
			if (strongest === void 0 || meetsIdentifierAuthentication(authentication, strongest)) strongest = authentication;
		}
		return strongest;
	};
}
function defaultNormalize(value) {
	return value;
}
function normalizeFieldValue(field, value, mode) {
	const normalized = (mode === "entry" ? field.normalizeEntry ?? field.normalize ?? defaultNormalize : field.normalizeSubject ?? field.normalize ?? defaultNormalize)(value);
	return normalized == null ? null : normalized.trim() || null;
}
function fieldDangerous(field, value) {
	return typeof field.dangerous === "function" ? field.dangerous(value) : field.dangerous;
}
function fieldAuthentication(field, value, dangerous) {
	return identifierAuthenticationFrom({
		authentication: typeof field.authentication === "function" ? field.authentication(value) : field.authentication,
		dangerous
	});
}
function identityFields(identity) {
	const fields = [{
		...identity.primary,
		key: identity.primary.key ?? "stableId",
		kind: identity.primary.kind ?? "stable-id"
	}];
	for (const alias of identity.aliases ?? []) fields.push({
		...alias,
		kind: alias.kind ?? `plugin:${alias.key}`
	});
	return fields;
}
function identityMatchKey(entry) {
	return `${entry.kind}:${entry.value}`;
}
function adapterEntry(params) {
	const dangerous = fieldDangerous(params.field, params.entry);
	return {
		opaqueEntryId: params.identity.resolveEntryId?.({
			entry: params.entry,
			entryIndex: params.entryIndex,
			fieldKey: params.field.key,
			fieldIndex: params.fieldIndex
		}) ?? `entry-${params.entryIndex + 1}:${params.fallbackSuffix ?? params.field.key}`,
		kind: params.field.kind,
		value: params.value,
		identityFieldKey: params.field.key,
		...params.wildcard ? { wildcard: true } : {},
		authentication: fieldAuthentication(params.field, params.entry, dangerous),
		dangerous,
		sensitivity: params.field.sensitivity
	};
}
function createIdentityAdapter(identity) {
	const fields = identityFields(identity);
	const isWildcardEntry = identity.isWildcardEntry ?? ((value) => value === "*");
	return {
		normalizeEntries({ entries }) {
			return {
				matchable: entries.flatMap((entry, entryIndex) => {
					if (isWildcardEntry(entry)) return [adapterEntry({
						identity,
						field: expectDefined(fields[0], "fields entry at 0"),
						fieldIndex: 0,
						entry,
						entryIndex,
						value: "*",
						fallbackSuffix: "wildcard",
						wildcard: true
					})];
					return fields.flatMap((field, fieldIndex) => {
						const value = normalizeFieldValue(field, entry, "entry");
						if (!value) return [];
						return [adapterEntry({
							identity,
							field,
							fieldIndex,
							entry,
							entryIndex,
							value
						})];
					});
				}),
				invalid: [],
				disabled: []
			};
		},
		matchSubject({ subject, entries, context }) {
			const normalizedSubjects = subject.identifiers.flatMap((identifier) => {
				const field = fields.find((candidate) => candidate.key === identifier.opaqueId && candidate.kind === identifier.kind);
				if (!field) return [];
				const value = normalizeFieldValue(field, identifier.value, "subject");
				return value ? [{
					identifier,
					value
				}] : [];
			});
			const matchedPairs = entries.flatMap((entry) => {
				const legacyMatch = identity.matchEntry?.({
					subject,
					entry,
					context
				});
				if (legacyMatch === false) return [];
				const candidates = entry.wildcard ? normalizedSubjects.filter(({ identifier }) => identifier.kind === fields[0]?.kind) : normalizedSubjects.filter(({ identifier, value }) => identifier.opaqueId === entry.identityFieldKey && identifier.kind === entry.kind && identityMatchKey({
					kind: identifier.kind,
					value
				}) === identityMatchKey(entry));
				if (candidates.length === 0) return legacyMatch === true ? [{
					opaqueEntryId: entry.opaqueEntryId,
					opaqueSubjectId: "legacy-subject-match",
					subjectAuthentication: "asserted"
				}] : entry.wildcard ? [{
					opaqueEntryId: entry.opaqueEntryId,
					opaqueSubjectId: "wildcard-subject",
					subjectAuthentication: "asserted"
				}] : [];
				return candidates.map(({ identifier }) => ({
					opaqueEntryId: entry.opaqueEntryId,
					opaqueSubjectId: identifier.opaqueId,
					subjectAuthentication: identifier.authentication
				}));
			});
			const matchedEntryIds = [...new Set(matchedPairs.map((pair) => pair.opaqueEntryId))];
			return {
				matched: matchedEntryIds.length > 0,
				matchedEntryIds,
				matchedPairs
			};
		}
	};
}
function createIdentitySubject(identity, input) {
	return { identifiers: identityFields(identity).flatMap((field, index) => {
		const rawValue = index === 0 ? input.stableId : input.aliases?.[field.key];
		if (rawValue == null) return [];
		const value = String(rawValue);
		const dangerous = fieldDangerous(field, value);
		const authentication = input.authentication !== void 0 ? input.authentication[field.key] ?? "unverified" : fieldAuthentication(field, value, dangerous);
		return [{
			opaqueId: field.key,
			kind: field.kind,
			value,
			authentication,
			dangerous,
			sensitivity: field.sensitivity
		}];
	}) };
}
//#endregion
//#region src/channels/message-access/state.ts
/**
* Channel ingress state resolver.
*
* Normalizes and matches route, sender, command, and access-group allowlists.
*/
function normalizeSubjectAuthentication(subject) {
	return { identifiers: subject.identifiers.map((identifier) => ({
		...identifier,
		authentication: identifierAuthenticationFrom(identifier)
	})) };
}
function redactedEntries(entries) {
	return entries.map(({ value: _value, identityFieldKey: _identityFieldKey, ...entry }) => entry);
}
function emptyMatch() {
	return {
		matched: false,
		matchedEntryIds: []
	};
}
function mergeMatches(matches) {
	const matchedEntryIds = uniqueStrings(matches.flatMap((match) => match.matchedEntryIds));
	const matchedPairs = matches.flatMap((match) => match.matchedPairs ?? []);
	const seenPairs = /* @__PURE__ */ new Set();
	const uniquePairs = matchedPairs.filter((pair) => {
		const key = JSON.stringify([
			pair.opaqueEntryId,
			pair.opaqueSubjectId,
			pair.subjectAuthentication
		]);
		if (seenPairs.has(key)) return false;
		seenPairs.add(key);
		return true;
	});
	return {
		matched: matches.some((match) => match.matched) || matchedEntryIds.length > 0,
		matchedEntryIds,
		...uniquePairs.length > 0 ? { matchedPairs: uniquePairs } : {}
	};
}
function mergeDiagnostics(...groups) {
	const diagnostics = [];
	for (const group of groups) if (group) diagnostics.push(...group);
	return diagnostics;
}
function accessGroupFactByName(facts) {
	return new Map((facts ?? []).map((fact) => [fact.groupName, fact]));
}
async function normalizeAndMatch(params) {
	if (params.entries.length === 0) return {
		normalizedEntries: [],
		invalidEntries: [],
		disabledEntries: [],
		match: emptyMatch()
	};
	const normalized = await params.adapter.normalizeEntries({
		entries: params.entries,
		context: params.context,
		accountId: params.accountId
	});
	const matchable = normalized.matchable.map((entry) => ({
		...entry,
		authentication: identifierAuthenticationFrom(entry)
	}));
	const match = matchable.length > 0 ? await params.adapter.matchSubject({
		subject: params.subject,
		entries: matchable,
		context: params.context
	}) : emptyMatch();
	return {
		normalizedEntries: redactedEntries(matchable),
		invalidEntries: normalized.invalid,
		disabledEntries: normalized.disabled,
		match
	};
}
function referencedAccessGroups(entries) {
	return Array.from(new Set(entries.map((entry) => parseAccessGroupAllowFromEntry(entry)).filter((entry) => entry != null)));
}
function directAllowlistEntries(entries) {
	return entries.filter((entry) => parseAccessGroupAllowFromEntry(entry) == null);
}
function groupSenderEntries(params) {
	const group = params.input.accessGroups?.[params.groupName];
	if (!group || group.type !== "message.senders") return [];
	return normalizeStringEntries([...group.members["*"] ?? [], ...group.members[params.input.channelId] ?? []]);
}
function eventSubjectMatchContext(input) {
	return input.conversation.kind === "direct" ? "dm" : "group";
}
async function normalizeSubjectIdentifiersForMatch(params) {
	return (await Promise.all(params.subject.identifiers.map(async (identifier, identifierIndex) => {
		return (await params.input.adapter.normalizeEntries({
			entries: [identifier.value],
			context: params.context,
			accountId: params.input.accountId
		})).matchable.filter((entry) => entry.kind === identifier.kind && (entry.identityFieldKey === void 0 || entry.identityFieldKey === identifier.opaqueId) && entry.value !== "*").map((entry, entryIndex) => ({
			opaqueEntryId: `${params.opaquePrefix}-${identifierIndex + 1}:${entryIndex + 1}`,
			kind: entry.kind,
			value: entry.value,
			identityFieldKey: entry.identityFieldKey,
			authentication: identifier.authentication,
			dangerous: entry.dangerous,
			sensitivity: entry.sensitivity
		}));
	}))).flat();
}
function strongerAuthentication(left, right) {
	if (!left) return right;
	return weakestIdentifierAuthentication(left, right) === left ? right : left;
}
function matchedAuthentication(params) {
	const entries = new Map(params.entries.map((entry) => [entry.opaqueEntryId, entry]));
	let strongest;
	for (const pair of params.match.matchedPairs ?? []) {
		const entry = entries.get(pair.opaqueEntryId);
		if (!entry) continue;
		const strength = weakestIdentifierAuthentication(entry.authentication, pair.subjectAuthentication);
		strongest = strongerAuthentication(strongest, strength);
	}
	if (!strongest) for (const entryId of params.match.matchedEntryIds) {
		const entry = entries.get(entryId);
		if (entry) strongest = strongerAuthentication(strongest, entry.authentication);
	}
	return strongest;
}
async function originSubjectAuthentication(input) {
	const origin = input.event.originSubject;
	if (!origin) return;
	let strongest;
	for (const originIdentifier of origin.identifiers) for (const current of input.subject.identifiers) {
		if (current.opaqueId !== originIdentifier.opaqueId || current.kind !== originIdentifier.kind || current.value !== originIdentifier.value) continue;
		strongest = strongerAuthentication(strongest, weakestIdentifierAuthentication(originIdentifier.authentication, current.authentication));
	}
	const context = eventSubjectMatchContext(input);
	const originEntries = await normalizeSubjectIdentifiersForMatch({
		input,
		subject: origin,
		context,
		opaquePrefix: "origin"
	});
	if (originEntries.length > 0) {
		const currentMatch = await input.adapter.matchSubject({
			subject: input.subject,
			entries: originEntries,
			context
		});
		if (currentMatch.matched) {
			const authentication = matchedAuthentication({
				entries: originEntries,
				match: currentMatch
			});
			if (authentication) strongest = strongerAuthentication(strongest, authentication);
		}
	}
	const currentEntries = await normalizeSubjectIdentifiersForMatch({
		input,
		subject: input.subject,
		context,
		opaquePrefix: "current"
	});
	if (currentEntries.length > 0) {
		const authentication = matchedAuthentication({
			entries: currentEntries,
			match: await input.adapter.matchSubject({
				subject: origin,
				entries: currentEntries,
				context
			})
		});
		if (authentication) strongest = strongerAuthentication(strongest, authentication);
	}
	return strongest;
}
async function resolveAccessGroupEntries(params) {
	const factByName = accessGroupFactByName(params.input.accessGroupMembership);
	const accessGroups = {
		referenced: [...params.referenced],
		matched: [],
		missing: [],
		unsupported: [],
		failed: []
	};
	const normalizedEntries = [];
	const invalidEntries = [];
	const disabledEntries = [];
	const matches = [];
	for (const groupName of params.referenced) {
		const fact = factByName.get(groupName);
		if (fact?.kind === "matched") {
			accessGroups.matched.push(groupName);
			for (const opaqueEntryId of fact.matchedEntryIds) normalizedEntries.push({
				opaqueEntryId,
				kind: "plugin:access-group-membership",
				authentication: "asserted"
			});
			matches.push({
				matched: true,
				matchedEntryIds: fact.matchedEntryIds
			});
			continue;
		}
		if (fact?.kind === "missing" || fact?.kind === "unsupported" || fact?.kind === "failed") {
			accessGroups[fact.kind].push(groupName);
			continue;
		}
		if (fact?.kind === "not-matched") continue;
		const group = params.input.accessGroups?.[groupName];
		if (!group) {
			accessGroups.missing.push(groupName);
			continue;
		}
		if (group.type !== "message.senders") {
			accessGroups.unsupported.push(groupName);
			continue;
		}
		const groupEntries = groupSenderEntries({
			groupName,
			input: params.input
		});
		const resolved = await normalizeAndMatch({
			adapter: params.input.adapter,
			subject: params.input.subject,
			accountId: params.input.accountId,
			entries: groupEntries,
			context: params.context
		});
		normalizedEntries.push(...resolved.normalizedEntries);
		invalidEntries.push(...resolved.invalidEntries);
		disabledEntries.push(...resolved.disabledEntries);
		if (resolved.match.matched) {
			accessGroups.matched.push(groupName);
			matches.push(resolved.match);
		}
	}
	return {
		normalizedEntries,
		invalidEntries,
		disabledEntries,
		matches,
		accessGroups
	};
}
async function resolveIngressAllowlist(params) {
	const entries = normalizeStringEntries(params.rawEntries ?? []);
	const referenced = referencedAccessGroups(entries);
	const directEntries = directAllowlistEntries(entries);
	const direct = await normalizeAndMatch({
		adapter: params.input.adapter,
		subject: params.input.subject,
		accountId: params.input.accountId,
		entries: directEntries,
		context: params.context
	});
	const groups = await resolveAccessGroupEntries({
		input: params.input,
		context: params.context,
		referenced
	});
	const match = mergeMatches([direct.match, ...groups.matches]);
	return {
		rawEntryCount: entries.length,
		normalizedEntries: [...direct.normalizedEntries, ...groups.normalizedEntries],
		invalidEntries: mergeDiagnostics(direct.invalidEntries, groups.invalidEntries),
		disabledEntries: mergeDiagnostics(direct.disabledEntries, groups.disabledEntries),
		matchedEntryIds: match.matchedEntryIds,
		hasConfiguredEntries: entries.length > 0,
		hasMatchableEntries: direct.normalizedEntries.length > 0 || groups.normalizedEntries.length > 0,
		hasWildcard: directEntries.includes("*"),
		accessGroups: groups.accessGroups,
		match
	};
}
async function resolveRouteFacts(input) {
	const routeFacts = [...input.routeFacts ?? []].toSorted((left, right) => left.precedence - right.precedence || left.id.localeCompare(right.id));
	const resolved = [];
	for (const route of routeFacts) {
		const senderAllowFrom = route.senderAllowFrom ?? (route.senderAllowFromSource === "effective-dm" ? input.allowlists.dm : route.senderAllowFromSource === "effective-group" ? input.allowlists.group : void 0);
		resolved.push({
			id: route.id,
			kind: route.kind,
			gate: route.gate,
			effect: route.effect,
			precedence: route.precedence,
			senderPolicy: route.senderPolicy,
			match: route.match,
			senderAllowlist: senderAllowFrom != null ? await resolveIngressAllowlist({
				input,
				rawEntries: senderAllowFrom,
				context: "route"
			}) : void 0
		});
	}
	return resolved;
}
async function resolveChannelIngressState(rawInput) {
	const input = {
		...rawInput,
		subject: normalizeSubjectAuthentication(rawInput.subject),
		event: {
			...rawInput.event,
			originSubject: rawInput.event.originSubject ? normalizeSubjectAuthentication(rawInput.event.originSubject) : void 0
		}
	};
	const [dm, pairingStore, group, commandOwner, commandGroup, routeFacts, eventOriginMatched] = await Promise.all([
		resolveIngressAllowlist({
			input,
			rawEntries: input.allowlists.dm,
			context: "dm"
		}),
		resolveIngressAllowlist({
			input,
			rawEntries: input.allowlists.pairingStore,
			context: "dm"
		}),
		resolveIngressAllowlist({
			input,
			rawEntries: input.allowlists.group,
			context: "group"
		}),
		resolveIngressAllowlist({
			input,
			rawEntries: input.allowlists.commandOwner,
			context: "command"
		}),
		resolveIngressAllowlist({
			input,
			rawEntries: input.allowlists.commandGroup,
			context: "command"
		}),
		resolveRouteFacts(input),
		originSubjectAuthentication(input)
	]);
	return {
		channelId: input.channelId,
		accountId: input.accountId,
		conversationKind: input.conversation.kind,
		event: {
			kind: input.event.kind,
			authMode: input.event.authMode,
			mayPair: input.event.mayPair,
			hasOriginSubject: input.event.originSubject != null,
			originSubjectMatched: eventOriginMatched !== void 0,
			...eventOriginMatched ? { originSubjectAuthentication: eventOriginMatched } : {}
		},
		mentionFacts: input.mentionFacts,
		routeFacts,
		allowlists: {
			dm,
			pairingStore,
			group,
			commandOwner,
			commandGroup
		}
	};
}
//#endregion
//#region src/channels/message-access/runtime.ts
/**
* Channel ingress runtime resolver.
*
* Merges route, sender, command, access-group, and pairing-store facts before decision evaluation.
*/
function shouldReadStore(params) {
	return params.conversationKind === "direct" && params.dmPolicy !== "allowlist" && params.dmPolicy !== "open";
}
async function readStoreAllowFrom(params) {
	if (!shouldReadStore({
		conversationKind: params.conversation.kind,
		dmPolicy: params.policy.dmPolicy
	})) return [];
	return [...(params.readStoreAllowFrom ? await params.readStoreAllowFrom({
		channelId: params.channelId,
		accountId: params.accountId,
		dmPolicy: params.policy.dmPolicy
	}).catch(() => []) : params.useDefaultPairingStore ? await readChannelIngressStoreAllowFromForDmPolicy({
		provider: params.channelId,
		accountId: params.accountId,
		dmPolicy: params.policy.dmPolicy
	}) : []) ?? []];
}
function commandRequested(policy) {
	return policy.command != null;
}
function normalizeChannelId(id) {
	const trimmed = id.trim();
	if (!trimmed) throw new Error("Channel ingress channel id must be non-empty.");
	return trimmed;
}
function findIngressGate(params) {
	return params.ingress.graph.gates.find((gate) => gate.phase === params.phase && gate.kind === params.kind);
}
function findSenderGate(ingress, isGroup) {
	return findIngressGate({
		ingress,
		phase: "sender",
		kind: isGroup ? "groupSender" : "dmSender"
	});
}
function useAccessGroupsFromConfig(params) {
	return params.useAccessGroups ?? true;
}
function channelIngressCommand(params = {}) {
	if (params.requested === false) return;
	const { requested: _requested, cfg, ...command } = params;
	return {
		...command,
		useAccessGroups: useAccessGroupsFromConfig({
			useAccessGroups: params.useAccessGroups,
			cfg
		}),
		allowTextCommands: params.allowTextCommands ?? false,
		hasControlCommand: params.hasControlCommand ?? true
	};
}
function channelIngressEvent(params = {}) {
	const isGroup = params.isGroup ?? false;
	return {
		kind: params.kind ?? "message",
		authMode: params.authMode ?? "inbound",
		mayPair: params.mayPair ?? !isGroup,
		...params.originSubject ? { originSubject: params.originSubject } : {}
	};
}
function resolveCommandInput(params) {
	if (params.command === false || params.command == null) return;
	return channelIngressCommand({
		...params.command,
		useAccessGroups: params.command.useAccessGroups ?? params.useAccessGroups
	});
}
function resolveResolverPolicy(params) {
	return {
		dmPolicy: params.input.dmPolicy ?? params.base.defaultDmPolicy ?? "pairing",
		groupPolicy: params.input.groupPolicy ?? params.base.defaultGroupPolicy ?? "disabled",
		groupAllowFromFallbackToAllowFrom: params.input.policy?.groupAllowFromFallbackToAllowFrom ?? params.base.groupAllowFromFallbackToAllowFrom,
		minIdentifierAuthentication: params.input.policy?.minIdentifierAuthentication ?? params.base.minIdentifierAuthentication,
		mutableIdentifierMatching: params.input.policy?.mutableIdentifierMatching ?? params.base.mutableIdentifierMatching,
		...params.input.policy?.activation ? { activation: params.input.policy.activation } : {}
	};
}
/**
* Create a reusable ingress resolver for one channel account and identity
* descriptor.
*/
function createChannelIngressResolver(base) {
	const resolve = async (input, eventDefaults) => {
		const isGroup = input.conversation.kind !== "direct";
		const useAccessGroups = useAccessGroupsFromConfig({
			useAccessGroups: base.useAccessGroups,
			cfg: base.cfg
		});
		return await resolveChannelMessageIngress({
			channelId: base.channelId,
			accountId: base.accountId,
			identity: base.identity,
			subject: input.subject,
			conversation: input.conversation,
			contextBinding: input.contextBinding,
			event: channelIngressEvent({
				isGroup,
				...eventDefaults,
				...input.event
			}),
			policy: resolveResolverPolicy({
				base,
				input
			}),
			allowFrom: input.allowFrom,
			groupAllowFrom: input.groupAllowFrom,
			route: input.route,
			routeFacts: input.routeFacts,
			accessGroups: base.accessGroups ?? base.cfg?.accessGroups,
			accessGroupMembership: [...base.accessGroupMembership ?? [], ...input.accessGroupMembership ?? []],
			resolveAccessGroupMembership: base.resolveAccessGroupMembership,
			accessGroupMatchedAllowFromEntry: base.accessGroupMatchedAllowFromEntry,
			providerMissingFallbackApplied: input.providerMissingFallbackApplied,
			mentionFacts: input.mentionFacts,
			readStoreAllowFrom: base.readStoreAllowFrom,
			useDefaultPairingStore: base.useDefaultPairingStore,
			command: resolveCommandInput({
				command: input.command,
				useAccessGroups
			})
		});
	};
	return {
		message: async (input) => await resolve(input),
		command: async (input) => await resolve(input, {
			authMode: "command",
			mayPair: false
		}),
		event: async (input) => await resolve(input, { mayPair: false })
	};
}
/**
* Resolve one inbound event using a simple stable subject identity descriptor.
*/
async function resolveStableChannelMessageIngress(params) {
	return await createChannelIngressResolver({
		...params,
		identity: defineStableChannelIngressIdentity(params.identity)
	}).message(params);
}
function routeDescriptors(route) {
	if (!route) return [];
	if (Array.isArray(route)) return [...route];
	return [route];
}
/**
* Collect optional route descriptors while dropping false, null, and undefined
* entries.
*/
function channelIngressRoutes(...routes) {
	return routes.filter((route) => Boolean(route));
}
function routeDescriptorMatch(descriptor) {
	const matched = descriptor.matched ?? descriptor.allowed ?? descriptor.enabled !== false;
	return {
		matched,
		matchedEntryIds: matched && descriptor.matchId ? [descriptor.matchId] : []
	};
}
function routeFact(params) {
	return {
		id: params.id,
		kind: params.kind ?? "route",
		gate: params.gate,
		effect: params.effect,
		precedence: params.precedence ?? 0,
		senderPolicy: params.senderPolicy ?? "inherit",
		senderAllowFrom: params.senderAllowFrom,
		senderAllowFromSource: params.senderAllowFromSource,
		match: params.match
	};
}
function routeFactDefaults(descriptor) {
	return {
		id: descriptor.id,
		...descriptor.kind ? { kind: descriptor.kind } : {},
		...descriptor.precedence !== void 0 ? { precedence: descriptor.precedence } : {},
		...descriptor.senderPolicy ? { senderPolicy: descriptor.senderPolicy } : {},
		...descriptor.senderAllowFrom != null ? { senderAllowFrom: [...descriptor.senderAllowFrom] } : {},
		...descriptor.senderAllowFromSource ? { senderAllowFromSource: descriptor.senderAllowFromSource } : {},
		match: routeDescriptorMatch(descriptor)
	};
}
function routeFactsFromDescriptors(route) {
	return routeDescriptors(route).flatMap((descriptor) => {
		if (descriptor.configured === false) return [];
		const defaults = routeFactDefaults(descriptor);
		if (descriptor.enabled === false) return [routeFact({
			...defaults,
			gate: "disabled",
			effect: "block-dispatch"
		})];
		if (descriptor.allowed !== void 0) return [routeFact({
			...defaults,
			gate: descriptor.allowed ? "matched" : "not-matched",
			effect: descriptor.allowed ? "allow" : "block-dispatch"
		})];
		if (descriptor.senderPolicy !== "deny-when-empty" && descriptor.senderAllowFrom == null && descriptor.senderAllowFromSource == null) return [];
		return [routeFact({
			...defaults,
			kind: descriptor.senderPolicy === "deny-when-empty" ? defaults.kind : "routeSender",
			gate: "matched",
			effect: "allow",
			senderPolicy: descriptor.senderPolicy === "deny-when-empty" ? "deny-when-empty" : defaults.senderPolicy
		})];
	});
}
function routeDescriptorForGate(params) {
	const baseGateId = params.gate.id.endsWith(":sender") ? params.gate.id.slice(0, -7) : params.gate.id;
	return params.descriptors.find((descriptor) => descriptor.id === params.gate.id || descriptor.id === baseGateId);
}
function projectRouteAccess(params) {
	const descriptors = routeDescriptors(params.route);
	const routeBlock = params.ingress.graph.gates.find((entry) => entry.phase === "route" && entry.effect === "block-dispatch");
	if (routeBlock) {
		const descriptor = routeDescriptorForGate({
			descriptors,
			gate: routeBlock
		});
		return {
			allowed: routeBlock.allowed,
			reasonCode: routeBlock.reasonCode,
			...descriptor?.blockReason ? { reason: descriptor.blockReason } : {},
			gate: routeBlock
		};
	}
	const routeSenderReplacement = descriptors.find((descriptor) => descriptor.senderPolicy === "replace" && descriptor.blockReason);
	const senderBlock = params.ingress.graph.gates.find((entry) => entry.phase === "sender" && entry.effect === "block-dispatch");
	if (routeSenderReplacement && senderBlock) return {
		allowed: false,
		reasonCode: senderBlock.reasonCode,
		reason: routeSenderReplacement.blockReason,
		gate: senderBlock
	};
	const gate = params.ingress.graph.gates.find((entry) => entry.phase === "route");
	if (gate) return {
		allowed: gate.allowed,
		reasonCode: gate.reasonCode,
		gate
	};
	return { allowed: true };
}
function projectSenderAccess(params) {
	const gate = findSenderGate(params.ingress, params.isGroup);
	const reasonCode = !gate && params.isGroup && params.ingress.reasonCode === "route_sender_empty" && params.effectiveGroupAllowFrom.length === 0 ? "group_policy_empty_allowlist" : gate?.reasonCode ?? params.ingress.reasonCode;
	const decision = reasonCode === "dm_policy_pairing_required" ? "pairing" : gate?.allowed === true ? "allow" : "block";
	return {
		allowed: decision === "allow",
		decision,
		reasonCode,
		...gate ? { gate } : {},
		effectiveAllowFrom: params.effectiveAllowFrom,
		effectiveGroupAllowFrom: params.effectiveGroupAllowFrom,
		providerMissingFallbackApplied: params.providerMissingFallbackApplied ?? false
	};
}
function projectCommandAccess(params) {
	const gate = findIngressGate({
		ingress: params.ingress,
		phase: "command",
		kind: "command"
	});
	return {
		requested: commandRequested(params.policy),
		authorized: commandRequested(params.policy) ? gate?.allowed === true : false,
		shouldBlockControlCommand: gate?.command?.shouldBlockControlCommand === true,
		reasonCode: gate?.reasonCode ?? params.ingress.reasonCode,
		...gate ? { gate } : {}
	};
}
function projectActivationAccess(params) {
	const gate = findIngressGate({
		ingress: params.ingress,
		phase: "activation",
		kind: "mention"
	});
	return {
		ran: gate != null,
		allowed: gate?.allowed === true,
		shouldSkip: gate?.activation?.shouldSkip === true,
		reasonCode: gate?.reasonCode ?? params.ingress.reasonCode,
		...gate?.activation?.effectiveWasMentioned !== void 0 ? { effectiveWasMentioned: gate.activation.effectiveWasMentioned } : {},
		...gate?.activation?.shouldBypassMention !== void 0 ? { shouldBypassMention: gate.activation.shouldBypassMention } : {},
		...gate ? { gate } : {}
	};
}
function commandOwnerAllowFrom(params) {
	if (params.command?.commandOwnerAllowFrom != null) return params.command.commandOwnerAllowFrom;
	if (!params.isGroup) return params.effectiveAllowFrom;
	return params.command?.groupOwnerAllowFrom === "none" ? [] : params.configuredAllowFrom;
}
function commandGroupAllowFrom(params) {
	if (params.isGroup) return params.effectiveCommandGroupAllowFrom;
	return params.command?.directGroupAllowFrom === "effective" ? params.effectiveCommandGroupAllowFrom : [];
}
function accessGroupMatchedEntry(params) {
	const entry = params.accessGroupMatchedAllowFromEntry ?? params.subject.stableId;
	return entry == null ? null : String(entry);
}
function appendAccessGroupMatchedEntry(params) {
	return params.matchedEntry && params.allowlist.accessGroups.matched.length > 0 ? uniqueStrings([...params.entries, params.matchedEntry]) : params.entries;
}
/**
* Resolve sender, route, command, event, and activation gates for one inbound
* channel event.
*/
async function resolveChannelMessageIngress(params) {
	const channelId = normalizeChannelId(params.channelId);
	const promptedAt = Date.now();
	const participantOwner = readChannelIngressHostOwner(channelId);
	const participant = params.identity.resolveParticipant?.(params.subject);
	const senderId = params.subject.stableId == null ? void 0 : String(params.subject.stableId);
	const participantBinding = params.contextBinding && { ...params.contextBinding };
	const adapter = createIdentityAdapter(params.identity);
	const subject = createIdentitySubject(params.identity, params.subject);
	const routeFacts = [...routeFactsFromDescriptors(params.route), ...params.routeFacts ?? []];
	const storeAllowFrom = await readStoreAllowFrom({
		...params,
		channelId
	});
	const rawAllowFrom = normalizeStringEntries(params.allowFrom ?? []);
	const rawStoreAllowFrom = normalizeStringEntries(storeAllowFrom);
	const rawGroupAllowFrom = normalizeStringEntries(params.groupAllowFrom ?? []);
	const normalizeEffective = (entries, context) => normalizeEffectiveEntries({
		adapter,
		accountId: params.accountId,
		entries,
		context
	});
	const [normalizedAllowFrom, normalizedStoreAllowFrom, normalizedGroupAllowFrom] = await Promise.all([
		normalizeEffective(rawAllowFrom, "dm"),
		normalizeEffective(rawStoreAllowFrom, "dm"),
		normalizeEffective(rawGroupAllowFrom, "group")
	]);
	const accessGroupMembership = [...await resolveRuntimeAccessGroupMembershipFacts({
		input: params,
		channelId,
		names: allReferencedAccessGroupNames([
			rawAllowFrom,
			rawGroupAllowFrom,
			rawStoreAllowFrom,
			params.command?.commandOwnerAllowFrom ?? [],
			...routeFacts.map((route) => route.senderAllowFrom ?? [])
		])
	}), ...params.accessGroupMembership ?? []];
	const baseEffective = resolveChannelIngressEffectiveAllowFromLists({
		allowFrom: normalizedAllowFrom,
		groupAllowFrom: normalizedGroupAllowFrom,
		storeAllowFrom: normalizedStoreAllowFrom,
		dmPolicy: params.policy.dmPolicy,
		groupAllowFromFallbackToAllowFrom: params.policy.groupAllowFromFallbackToAllowFrom
	});
	const rawEffective = resolveChannelIngressEffectiveAllowFromLists({
		allowFrom: rawAllowFrom,
		groupAllowFrom: rawGroupAllowFrom,
		storeAllowFrom: rawStoreAllowFrom,
		dmPolicy: params.policy.dmPolicy,
		groupAllowFromFallbackToAllowFrom: params.policy.groupAllowFromFallbackToAllowFrom
	});
	const rawCommandGroup = resolveChannelIngressEffectiveAllowFromLists({
		allowFrom: rawAllowFrom,
		groupAllowFrom: rawGroupAllowFrom,
		dmPolicy: params.policy.dmPolicy,
		groupAllowFromFallbackToAllowFrom: params.command?.commandGroupAllowFromFallbackToAllowFrom ?? params.policy.groupAllowFromFallbackToAllowFrom
	});
	const isGroup = params.conversation.kind !== "direct";
	const policy = {
		...params.policy,
		...params.command !== void 0 ? { command: params.command } : {}
	};
	const state = await resolveChannelIngressState({
		channelId,
		accountId: params.accountId,
		subject,
		conversation: params.conversation,
		adapter,
		accessGroups: params.accessGroups,
		accessGroupMembership,
		routeFacts,
		mentionFacts: params.mentionFacts,
		event: params.event,
		allowlists: {
			dm: rawAllowFrom,
			group: rawEffective.effectiveGroupAllowFrom,
			pairingStore: rawStoreAllowFrom,
			commandOwner: commandOwnerAllowFrom({
				command: params.command,
				isGroup,
				configuredAllowFrom: rawAllowFrom,
				effectiveAllowFrom: rawEffective.effectiveAllowFrom
			}),
			commandGroup: commandGroupAllowFrom({
				command: params.command,
				isGroup,
				effectiveCommandGroupAllowFrom: rawCommandGroup.effectiveGroupAllowFrom
			})
		}
	});
	const ingress = decideChannelIngress(state, policy);
	const matchedAccessGroupEntry = accessGroupMatchedEntry(params);
	const senderAccess = projectSenderAccess({
		ingress,
		isGroup,
		effectiveAllowFrom: appendAccessGroupMatchedEntry({
			entries: baseEffective.effectiveAllowFrom,
			allowlist: state.allowlists.dm,
			matchedEntry: matchedAccessGroupEntry
		}),
		effectiveGroupAllowFrom: appendAccessGroupMatchedEntry({
			entries: baseEffective.effectiveGroupAllowFrom,
			allowlist: state.allowlists.group,
			matchedEntry: matchedAccessGroupEntry
		}),
		providerMissingFallbackApplied: params.providerMissingFallbackApplied
	});
	const result = {
		state,
		ingress,
		senderAccess,
		routeAccess: projectRouteAccess({
			ingress,
			route: params.route
		}),
		commandAccess: projectCommandAccess({
			ingress,
			policy
		}),
		activationAccess: projectActivationAccess({ ingress })
	};
	if ((participant || senderId) && participantBinding && participantOwner && ingress.admission === "dispatch") prepareChannelParticipantInput(result, {
		identity: participant ? {
			type: "remote",
			pluginId: channelId,
			domain: participant.domain,
			idKind: participant.idKind,
			id: participant.id
		} : {
			type: "observation",
			pluginId: channelId,
			accountId: params.accountId ?? null,
			senderKind: "unknown",
			id: senderId
		},
		binding: participantBinding,
		promptedAt,
		owner: participantOwner
	});
	return recordChannelIngressResolution({
		result,
		channelId,
		accountId: params.accountId,
		rawPrincipalRef: params.subject.stableId,
		scope: {
			conversation: {
				kind: params.conversation.kind,
				id: params.conversation.id,
				parentId: params.conversation.parentId,
				threadId: params.conversation.threadId
			},
			contextBinding: params.contextBinding
		},
		participantOutcomeAffecting: senderAccess.gate?.match?.matched === true && (senderAccess.reasonCode === "dm_policy_allowlisted" || senderAccess.reasonCode === "group_policy_allowed") && !(isGroup ? state.allowlists.group.hasWildcard : state.allowlists.dm.hasWildcard || state.allowlists.pairingStore.hasWildcard),
		identifierAuthentication: ingress.graph.gates.some((gate) => gate.identifierAuthentication?.affectedMatch) ? "affected" : ingress.graph.gates.some((gate) => gate.identifierAuthentication?.evaluated) ? "evaluated" : "not-evaluated"
	});
}
//#endregion
//#region src/config/implicit-mentions.ts
const SHIPPED_IMPLICIT_MENTION_DEFAULTS = {
	replyToBot: true,
	quotedBot: true,
	threadParticipation: true
};
/** Resolves each implicit-mention kind using account, channel, defaults, then shipped behavior. */
function resolveChannelImplicitMentions(params) {
	const channelConfig = params.cfg.channels?.[params.channel];
	const accountConfig = resolveAccountEntry(channelConfig?.accounts, normalizeAccountId(params.accountId));
	const defaults = params.cfg.channels?.defaults?.implicitMentions;
	return {
		replyToBot: accountConfig?.implicitMentions?.replyToBot ?? channelConfig?.implicitMentions?.replyToBot ?? defaults?.replyToBot ?? SHIPPED_IMPLICIT_MENTION_DEFAULTS.replyToBot,
		quotedBot: accountConfig?.implicitMentions?.quotedBot ?? channelConfig?.implicitMentions?.quotedBot ?? defaults?.quotedBot ?? SHIPPED_IMPLICIT_MENTION_DEFAULTS.quotedBot,
		threadParticipation: accountConfig?.implicitMentions?.threadParticipation ?? channelConfig?.implicitMentions?.threadParticipation ?? defaults?.threadParticipation ?? SHIPPED_IMPLICIT_MENTION_DEFAULTS.threadParticipation
	};
}
//#endregion
//#region src/plugin-sdk/channel-ingress-runtime.ts
/**
* High-level runtime resolver for inbound channel access decisions.
*
* Channel plugins should use this subpath for new receive paths. It accepts
* platform facts, raw allowlists, route descriptors, command facts, and access
* group config, then returns sender/route/command/activation projections plus
* the ordered ingress graph.
*/
/** Version-1 raw events, 500 ms polling, eight deliveries, and standard retention. */
function createStandardRawEventIngressMonitor(options) {
	const { classifyAdmissionError, createStoppedError, drain, payload, pollIntervalMs, ...base } = options;
	const stoppedError = createStoppedError ?? (() => /* @__PURE__ */ new Error("Channel ingress monitor is stopped."));
	const monitor = createChannelIngressMonitor({
		...base,
		inspect: options.inspect,
		payload: {
			...payload,
			storage: "raw-event",
			version: 1
		},
		pollIntervalMs: pollIntervalMs ?? 500,
		retention: "standard",
		drain: {
			...drain,
			startLimit: 8
		},
		admissionMode: "while-running",
		createStoppedError: stoppedError
	});
	return {
		receive: async (raw) => {
			if (!monitor.isRunning()) throw stoppedError();
			let facts;
			try {
				facts = options.inspect(raw);
			} catch (error) {
				const message = classifyAdmissionError(error);
				if (message === void 0) throw error;
				return {
					kind: "invalid",
					message
				};
			}
			if (!facts) return { kind: "ignored" };
			await monitor.admit(raw, { facts });
			return { kind: "durable" };
		},
		start: monitor.start,
		stop: monitor.stop,
		waitForIdle: monitor.waitForIdle
	};
}
/** Fan one logical inbound turn's ownership lifecycle across its durable claims. */
function fanInChannelIngressLifecycles(inputs) {
	const lifecycles = inputs.filter((lifecycle) => lifecycle !== void 0);
	const first = lifecycles[0];
	if (!first) return {
		lifecycle: void 0,
		settle: async () => {},
		abandon: async () => {},
		cancel: async () => {}
	};
	let handedOff = false;
	const adoptAll = async () => {
		for (const lifecycle of lifecycles) await lifecycle.onAdopted();
	};
	const fanOut = async (invoke) => {
		await Promise.all(lifecycles.map(async (lifecycle) => await invoke(lifecycle)));
	};
	const abandonAll = () => fanOut((lifecycle) => lifecycle.onAbandoned());
	const failAll = (error) => fanOut((lifecycle) => lifecycle.onFailed ? lifecycle.onFailed(error) : lifecycle.onAbandoned());
	const supportsCancellation = lifecycles.every((lifecycle) => lifecycle.onCancelled !== void 0);
	const cancelAll = () => fanOut((lifecycle) => lifecycle.onCancelled ? lifecycle.onCancelled() : lifecycle.onAbandoned());
	return {
		lifecycle: {
			abortSignal: lifecycles.length === 1 ? first.abortSignal : AbortSignal.any(lifecycles.map((lifecycle) => lifecycle.abortSignal)),
			onAdopted: async () => {
				handedOff = true;
				await adoptAll();
			},
			onDeferred: () => {
				handedOff = true;
				for (const lifecycle of lifecycles) lifecycle.onDeferred();
			},
			onDeferredHeartbeat: () => {
				for (const lifecycle of lifecycles) lifecycle.onDeferredHeartbeat?.();
			},
			onAdoptionFinalizing: () => {
				for (const lifecycle of lifecycles) lifecycle.onAdoptionFinalizing();
			},
			onFailed: async (error) => {
				handedOff = true;
				await failAll(error);
			},
			...supportsCancellation ? { onCancelled: async () => {
				handedOff = true;
				await cancelAll();
			} } : {},
			onAbandoned: async () => {
				handedOff = true;
				await abandonAll();
			}
		},
		settle: async () => {
			if (!handedOff) {
				await adoptAll();
				handedOff = true;
			}
		},
		abandon: async (error) => {
			if (!handedOff) {
				handedOff = true;
				await (error === void 0 ? abandonAll() : failAll(error));
			}
		},
		cancel: async () => {
			if (!handedOff) {
				handedOff = true;
				await cancelAll();
			}
		}
	};
}
//#endregion
export { createChannelIngressResolver as a, defineStableChannelIngressIdentity as c, channelIngressRoutes as i, identityEntryAuthenticationClassifier as l, fanInChannelIngressLifecycles as n, resolveChannelMessageIngress as o, resolveChannelImplicitMentions as r, resolveStableChannelMessageIngress as s, createStandardRawEventIngressMonitor as t, meetsIdentifierAuthentication as u };