UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,639 lines 66.9 kB
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { l as normalizeStringEntries } from "./string-normalization-WNUDCpXX.js";
import { s as hasConfiguredSecretInput } from "./types.secrets-_0JOMGE5.js";
import { An as preprocess, At as boolean, Et as array, Nn as record, Rn as string, Si as NEVER, Tn as object, Xn as union, Zn as unknown, dn as literal, wn as number, yt as _enum } from "./schemas-6cH6bZ7o.js";
import { n as ZodIssueCode } from "./compat-B-THjfQ8.js";
import { C as RetryConfigSchema, E as SecretRefSchema, M as TtsConfigSchema, R as requireAllowlistAllowFrom, S as ReplyToModeSchema, a as DmConfigSchema, g as MentionPatternsPolicySchema, h as MarkdownConfigSchema, i as ContextVisibilityModeSchema, l as GroupPolicySchema, m as MSTeamsReplyStyleSchema, n as BlockStreamingCoalesceSchema, o as DmPolicySchema, s as ExecutableTokenSchema, t as BlockStreamingChunkSchema, u as HexColorSchema, w as SecretInputSchema, y as ProviderCommandsSchema, z as requireOpenAllowFrom } from "./zod-schema.core-1wQTyhOa.js";
import { t as sensitive } from "./zod-schema.sensitive-NH5cJgEj.js";
import "./config-schema-CO2ikh7C.js";
import { l as ToolPolicySchema } from "./zod-schema.agent-runtime-BTWfpsXE.js";
import { i as ChannelHeartbeatVisibilitySchema, o as NativeExecApprovalEnableModeSchema, r as ChannelHealthMonitorSchema, t as ChannelBotLoopProtectionSchema } from "./zod-schema.channels-config-CYFj02nK.js";
import { t as resolveAccountEntry } from "./account-lookup-DL1YTqjF.js";
import { n as isValidInboundPathRootPattern } from "./inbound-path-policy-CYWsER5a.js";
import { n as normalizeSlashCommandName, r as resolveCustomCommands, t as normalizeCommandDescription } from "./custom-command-config-D5_ubr78.js";
import { t as isSafeScpRemoteHost } from "./scp-host-D4uXCtRP.js";
//#region src/config/zod-schema.secret-input-validation.ts
function forEachEnabledAccount(accounts, run) {
	if (!accounts) return;
	for (const [accountId, account] of Object.entries(accounts)) {
		if (!account || account.enabled === false) continue;
		run(accountId, account);
	}
}
/** Validates Telegram webhook URLs have a usable shared or account webhook secret. */
function validateTelegramWebhookSecretRequirements(value, ctx) {
	const baseWebhookUrl = normalizeOptionalString(value.webhookUrl) ?? "";
	const hasBaseWebhookSecret = hasConfiguredSecretInput(value.webhookSecret);
	if (baseWebhookUrl && !hasBaseWebhookSecret) ctx.addIssue({
		code: ZodIssueCode.custom,
		message: "channels.telegram.webhookUrl requires channels.telegram.webhookSecret",
		path: ["webhookSecret"]
	});
	forEachEnabledAccount(value.accounts, (accountId, account) => {
		if (!(normalizeOptionalString(account.webhookUrl) ?? "")) return;
		if (!hasConfiguredSecretInput(account.webhookSecret) && !hasBaseWebhookSecret) ctx.addIssue({
			code: ZodIssueCode.custom,
			message: "channels.telegram.accounts.*.webhookUrl requires channels.telegram.webhookSecret or channels.telegram.accounts.*.webhookSecret",
			path: [
				"accounts",
				accountId,
				"webhookSecret"
			]
		});
	});
}
function validateSlackSigningSecretRequirements(value, ctx) {
	const baseMode = value.mode === "http" || value.mode === "socket" ? value.mode : "socket";
	if (baseMode === "http" && !hasConfiguredSecretInput(value.signingSecret)) ctx.addIssue({
		code: ZodIssueCode.custom,
		message: "channels.slack.mode=\"http\" requires channels.slack.signingSecret",
		path: ["signingSecret"]
	});
	forEachEnabledAccount(value.accounts, (accountId, account) => {
		if ((account.mode === "http" || account.mode === "socket" ? account.mode : baseMode) !== "http") return;
		if (!hasConfiguredSecretInput(account.signingSecret ?? value.signingSecret)) ctx.addIssue({
			code: ZodIssueCode.custom,
			message: "channels.slack.accounts.*.mode=\"http\" requires channels.slack.signingSecret or channels.slack.accounts.*.signingSecret",
			path: [
				"accounts",
				accountId,
				"signingSecret"
			]
		});
	});
}
//#endregion
//#region src/config/zod-schema.providers-core.ts
const ToolPolicyBySenderSchema$1 = record(string(), ToolPolicySchema).optional();
const DiscordIdSchema = union([string(), number()]).transform((value, ctx) => {
	if (typeof value === "number") {
		if (!Number.isSafeInteger(value) || value < 0) {
			ctx.addIssue({
				code: ZodIssueCode.custom,
				message: `Discord ID "${String(value)}" is not a valid non-negative safe integer. Wrap it in quotes in your config file.`
			});
			return NEVER;
		}
		return String(value);
	}
	return value;
}).pipe(string());
const DiscordIdListSchema = array(DiscordIdSchema);
const DiscordSnowflakeStringSchema = string().regex(/^\d+$/, "Discord user ID must be numeric");
const TelegramInlineButtonsScopeSchema = _enum([
	"off",
	"dm",
	"group",
	"all",
	"allowlist"
]);
const TelegramIdListSchema = array(union([string(), number()]));
const TelegramCapabilitiesSchema = union([array(string()), object({ inlineButtons: TelegramInlineButtonsScopeSchema.optional() }).strict()]);
const TextChunkModeSchema = _enum(["length", "newline"]);
const UnifiedStreamingModeSchema = _enum([
	"off",
	"partial",
	"block",
	"progress"
]);
const ChannelStreamingBlockSchema = object({
	enabled: boolean().optional(),
	coalesce: BlockStreamingCoalesceSchema.optional()
}).strict();
const ChannelStreamingPreviewSchema = object({
	chunk: BlockStreamingChunkSchema.optional(),
	toolProgress: boolean().optional(),
	commandText: _enum(["raw", "status"]).optional()
}).strict();
const TelegramStreamingPreviewSchema = ChannelStreamingPreviewSchema.extend({
	nativeToolProgress: boolean().optional(),
	nativeToolProgressAllowFrom: array(union([string(), number()])).optional()
}).strict();
const ChannelStreamingProgressSchema = object({
	label: union([string(), literal(false)]).optional(),
	labels: array(string()).optional(),
	maxLines: number().int().positive().optional(),
	maxLineChars: number().int().positive().optional(),
	render: _enum(["text", "rich"]).optional(),
	toolProgress: boolean().optional(),
	commandText: _enum(["raw", "status"]).optional(),
	commentary: boolean().optional()
}).strict();
const SlackStreamingProgressSchema = ChannelStreamingProgressSchema.extend({ nativeTaskCards: boolean().optional() }).strict();
const ChannelDeliveryStreamingConfigSchema = object({
	chunkMode: TextChunkModeSchema.optional(),
	block: ChannelStreamingBlockSchema.optional()
}).strict();
const ChannelPreviewStreamingConfigSchema = object({
	mode: UnifiedStreamingModeSchema.optional(),
	chunkMode: TextChunkModeSchema.optional(),
	preview: ChannelStreamingPreviewSchema.optional(),
	progress: ChannelStreamingProgressSchema.optional(),
	block: ChannelStreamingBlockSchema.optional()
}).strict();
const TelegramPreviewStreamingConfigSchema = ChannelPreviewStreamingConfigSchema.extend({ preview: TelegramStreamingPreviewSchema.optional() }).strict();
const DiscordPreviewStreamingConfigSchema = ChannelPreviewStreamingConfigSchema;
const SlackStreamingConfigSchema = ChannelPreviewStreamingConfigSchema.extend({
	nativeTransport: boolean().optional(),
	progress: SlackStreamingProgressSchema.optional()
}).strict();
const SlackCapabilitiesSchema = union([array(string()), object({ interactiveReplies: boolean().optional() }).strict()]);
const BotLoopProtectionSchema = object({
	enabled: boolean().optional(),
	maxEventsPerWindow: number().int().positive().optional(),
	windowSeconds: number().int().positive().optional(),
	cooldownSeconds: number().int().positive().optional()
}).strict();
const TelegramErrorPolicySchema = _enum([
	"always",
	"once",
	"silent"
]).optional();
const TelegramCustomCommandConfig = {
	label: "Telegram",
	pattern: /^[a-z0-9_]{1,32}$/,
	patternDescription: "use a-z, 0-9, underscore; max 32 chars"
};
const TelegramTopicSchema = object({
	requireMention: boolean().optional(),
	ingest: boolean().optional(),
	disableAudioPreflight: boolean().optional(),
	groupPolicy: GroupPolicySchema.optional(),
	skills: array(string()).optional(),
	enabled: boolean().optional(),
	allowFrom: array(union([string(), number()])).optional(),
	systemPrompt: string().optional(),
	agentId: string().optional(),
	errorPolicy: TelegramErrorPolicySchema,
	errorCooldownMs: number().int().nonnegative().optional()
}).strict();
const TelegramGroupSchema = object({
	requireMention: boolean().optional(),
	ingest: boolean().optional(),
	disableAudioPreflight: boolean().optional(),
	groupPolicy: GroupPolicySchema.optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema$1,
	skills: array(string()).optional(),
	enabled: boolean().optional(),
	allowFrom: array(union([string(), number()])).optional(),
	systemPrompt: string().optional(),
	topics: record(string(), TelegramTopicSchema.optional()).optional(),
	errorPolicy: TelegramErrorPolicySchema,
	errorCooldownMs: number().int().nonnegative().optional()
}).strict();
const AutoTopicLabelSchema = union([boolean(), object({
	enabled: boolean().optional(),
	prompt: string().optional()
}).strict()]).optional();
const TelegramDirectSchema = object({
	dmPolicy: DmPolicySchema.optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema$1,
	skills: array(string()).optional(),
	enabled: boolean().optional(),
	allowFrom: array(union([string(), number()])).optional(),
	systemPrompt: string().optional(),
	topics: record(string(), TelegramTopicSchema.optional()).optional(),
	errorPolicy: TelegramErrorPolicySchema,
	errorCooldownMs: number().int().nonnegative().optional(),
	requireTopic: boolean().optional(),
	autoTopicLabel: AutoTopicLabelSchema
}).strict();
const TelegramCustomCommandSchema = object({
	command: string().overwrite(normalizeSlashCommandName),
	description: string().overwrite(normalizeCommandDescription)
}).strict();
const validateTelegramCustomCommands = (value, ctx) => {
	if (!value.customCommands || value.customCommands.length === 0) return;
	const { issues } = resolveCustomCommands({
		commands: value.customCommands,
		checkReserved: false,
		checkDuplicates: false,
		config: TelegramCustomCommandConfig
	});
	for (const issue of issues) ctx.addIssue({
		code: ZodIssueCode.custom,
		path: [
			"customCommands",
			issue.index,
			issue.field
		],
		message: issue.message
	});
};
const TelegramAccountSchemaBase = object({
	name: string().optional(),
	capabilities: TelegramCapabilitiesSchema.optional(),
	execApprovals: object({
		enabled: NativeExecApprovalEnableModeSchema.optional(),
		approvers: TelegramIdListSchema.optional(),
		agentFilter: array(string()).optional(),
		sessionFilter: array(string()).optional(),
		target: _enum([
			"dm",
			"channel",
			"both"
		]).optional()
	}).strict().optional(),
	markdown: MarkdownConfigSchema,
	enabled: boolean().optional(),
	commands: ProviderCommandsSchema,
	customCommands: array(TelegramCustomCommandSchema).optional(),
	configWrites: boolean().optional(),
	dmPolicy: DmPolicySchema.optional().default("pairing"),
	botToken: SecretInputSchema.optional().register(sensitive),
	tokenFile: string().optional(),
	replyToMode: ReplyToModeSchema.optional(),
	groups: record(string(), TelegramGroupSchema.optional()).optional(),
	allowFrom: array(union([string(), number()])).optional(),
	defaultTo: union([string(), number()]).optional(),
	groupAllowFrom: array(union([string(), number()])).optional(),
	groupPolicy: GroupPolicySchema.optional().default("allowlist"),
	mentionPatterns: MentionPatternsPolicySchema.optional(),
	contextVisibility: ContextVisibilityModeSchema.optional(),
	historyLimit: number().int().min(0).optional(),
	dmHistoryLimit: number().int().min(0).optional(),
	dms: record(string(), DmConfigSchema.optional()).optional(),
	direct: record(string(), TelegramDirectSchema.optional()).optional(),
	textChunkLimit: number().int().positive().optional(),
	streaming: TelegramPreviewStreamingConfigSchema.optional(),
	mediaMaxMb: number().positive().optional(),
	timeoutSeconds: number().int().positive().optional(),
	mediaGroupFlushMs: number().int().min(10).max(6e4).optional().describe("Buffer window in milliseconds for Telegram media groups/albums before dispatching them as one inbound message. Default: 500."),
	pollingStallThresholdMs: number().int().min(3e4).max(6e5).optional(),
	retry: RetryConfigSchema,
	network: object({
		autoSelectFamily: boolean().optional(),
		dnsResultOrder: _enum(["ipv4first", "verbatim"]).optional(),
		dangerouslyAllowPrivateNetwork: boolean().optional().describe("Dangerous opt-in for trusted Telegram fake-IP or transparent-proxy environments where api.telegram.org resolves to private/internal/special-use addresses during media downloads.")
	}).strict().optional(),
	proxy: string().optional(),
	webhookUrl: string().optional().describe("Public HTTPS webhook URL registered with Telegram for inbound updates. This must be internet-reachable and requires channels.telegram.webhookSecret."),
	webhookSecret: SecretInputSchema.optional().describe("Secret token sent to Telegram during webhook registration and verified on inbound webhook requests. Telegram returns this value for verification; this is not the gateway auth token and not the bot token.").register(sensitive),
	webhookPath: string().optional().describe("Local webhook route path served by the gateway listener. Defaults to /telegram-webhook."),
	webhookHost: string().optional().describe("Local bind host for the webhook listener. Defaults to 127.0.0.1; keep loopback unless you intentionally expose direct ingress."),
	webhookPort: number().int().nonnegative().optional().describe("Local bind port for the webhook listener. Defaults to 8787; set to 0 to let the OS assign an ephemeral port."),
	webhookCertPath: string().optional().describe("Path to the self-signed certificate (PEM) to upload to Telegram during webhook registration. Required for self-signed certs (direct IP or no domain)."),
	actions: object({
		reactions: boolean().optional(),
		sendMessage: boolean().optional(),
		poll: boolean().optional(),
		deleteMessage: boolean().optional(),
		editMessage: boolean().optional(),
		sticker: boolean().optional(),
		createForumTopic: boolean().optional(),
		editForumTopic: boolean().optional()
	}).strict().optional(),
	threadBindings: object({
		enabled: boolean().optional(),
		idleHours: number().nonnegative().optional(),
		maxAgeHours: number().nonnegative().optional(),
		spawnSessions: boolean().optional(),
		defaultSpawnContext: _enum(["isolated", "fork"]).optional(),
		spawnSubagentSessions: boolean().optional(),
		spawnAcpSessions: boolean().optional()
	}).strict().optional(),
	reactionNotifications: _enum([
		"off",
		"own",
		"all"
	]).optional(),
	reactionLevel: _enum([
		"off",
		"ack",
		"minimal",
		"extensive"
	]).optional(),
	heartbeat: ChannelHeartbeatVisibilitySchema,
	healthMonitor: ChannelHealthMonitorSchema,
	linkPreview: boolean().optional(),
	silentErrorReplies: boolean().optional(),
	responsePrefix: string().optional(),
	ackReaction: string().optional(),
	errorPolicy: TelegramErrorPolicySchema,
	errorCooldownMs: number().int().nonnegative().optional(),
	apiRoot: string().url().optional(),
	trustedLocalFileRoots: array(string()).optional().describe("Trusted local filesystem roots for self-hosted Telegram Bot API absolute file_path values. Only absolute paths under these roots are read directly; all other absolute paths are rejected."),
	autoTopicLabel: AutoTopicLabelSchema
}).strict();
const TelegramAccountSchema = TelegramAccountSchemaBase.superRefine((value, ctx) => {
	validateTelegramCustomCommands(value, ctx);
});
const TelegramConfigSchema = TelegramAccountSchemaBase.extend({
	accounts: record(string(), TelegramAccountSchema.optional()).optional(),
	defaultAccount: string().optional()
}).superRefine((value, ctx) => {
	requireOpenAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.telegram.dmPolicy=\"open\" requires channels.telegram.allowFrom to include \"*\""
	});
	requireAllowlistAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.telegram.dmPolicy=\"allowlist\" requires channels.telegram.allowFrom to contain at least one sender ID"
	});
	validateTelegramCustomCommands(value, ctx);
	if (value.accounts) for (const [accountId, account] of Object.entries(value.accounts)) {
		if (!account) continue;
		const effectivePolicy = account.dmPolicy ?? value.dmPolicy;
		const effectiveAllowFrom = account.allowFrom ?? value.allowFrom;
		requireOpenAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.telegram.accounts.*.dmPolicy=\"open\" requires channels.telegram.accounts.*.allowFrom (or channels.telegram.allowFrom) to include \"*\""
		});
		requireAllowlistAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.telegram.accounts.*.dmPolicy=\"allowlist\" requires channels.telegram.accounts.*.allowFrom (or channels.telegram.allowFrom) to contain at least one sender ID"
		});
	}
	if (!value.accounts) {
		validateTelegramWebhookSecretRequirements(value, ctx);
		return;
	}
	for (const [accountId, account] of Object.entries(value.accounts)) {
		if (!account) continue;
		if (account.enabled === false) continue;
		const effectiveDmPolicy = account.dmPolicy ?? value.dmPolicy;
		const effectiveAllowFrom = Array.isArray(account.allowFrom) ? account.allowFrom : value.allowFrom;
		requireOpenAllowFrom({
			policy: effectiveDmPolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.telegram.accounts.*.dmPolicy=\"open\" requires channels.telegram.allowFrom or channels.telegram.accounts.*.allowFrom to include \"*\""
		});
		requireAllowlistAllowFrom({
			policy: effectiveDmPolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.telegram.accounts.*.dmPolicy=\"allowlist\" requires channels.telegram.allowFrom or channels.telegram.accounts.*.allowFrom to contain at least one sender ID"
		});
	}
	validateTelegramWebhookSecretRequirements(value, ctx);
});
const DiscordDmSchema = object({
	enabled: boolean().optional(),
	policy: DmPolicySchema.optional(),
	allowFrom: DiscordIdListSchema.optional(),
	groupEnabled: boolean().optional(),
	groupChannels: DiscordIdListSchema.optional()
}).strict();
const DiscordThreadSchema = object({ inheritParent: boolean().optional() }).strict();
const DiscordGuildChannelSchema = object({
	requireMention: boolean().optional(),
	ignoreOtherMentions: boolean().optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema$1,
	skills: array(string()).optional(),
	enabled: boolean().optional(),
	users: DiscordIdListSchema.optional(),
	roles: DiscordIdListSchema.optional(),
	systemPrompt: string().optional(),
	includeThreadStarter: boolean().optional(),
	autoThread: boolean().optional(),
	/** Naming strategy for auto-created threads. "message" uses message text; "generated" creates an LLM title after thread creation. */
	autoThreadName: _enum(["message", "generated"]).optional(),
	/** Archive duration for auto-created threads in minutes. Discord supports 60, 1440 (1 day), 4320 (3 days), 10080 (1 week). Default: 60. */
	autoArchiveDuration: union([
		_enum([
			"60",
			"1440",
			"4320",
			"10080"
		]),
		literal(60),
		literal(1440),
		literal(4320),
		literal(10080)
	]).optional()
}).strict();
const DiscordGuildSchema = object({
	slug: string().optional(),
	requireMention: boolean().optional(),
	ignoreOtherMentions: boolean().optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema$1,
	reactionNotifications: _enum([
		"off",
		"own",
		"all",
		"allowlist"
	]).optional(),
	users: DiscordIdListSchema.optional(),
	roles: DiscordIdListSchema.optional(),
	channels: record(string(), DiscordGuildChannelSchema.optional()).optional()
}).strict();
const DiscordUiSchema = object({ components: object({ accentColor: HexColorSchema.optional() }).strict().optional() }).strict().optional();
const DiscordVoiceAutoJoinSchema = object({
	guildId: string().min(1),
	channelId: string().min(1)
}).strict();
const DiscordVoiceAllowedChannelSchema = object({
	guildId: string().min(1),
	channelId: string().min(1)
}).strict();
const DiscordVoiceRealtimeToolPolicySchema = _enum([
	"safe-read-only",
	"owner",
	"none"
]);
const DiscordVoiceRealtimeConsultPolicySchema = _enum(["auto", "always"]);
const DiscordVoiceRealtimeBootstrapContextFileSchema = _enum([
	"IDENTITY.md",
	"USER.md",
	"SOUL.md"
]);
const DiscordVoiceRealtimeWakeNameSchema = string().min(1).regex(/^\s*[^a-z0-9]*[a-z0-9]+(?:[^a-z0-9]+[a-z0-9]+)?[^a-z0-9]*\s*$/i, { message: "Discord realtime wake names must be one or two words." });
const DiscordVoiceRealtimeSchema = object({
	provider: string().min(1).optional(),
	model: string().min(1).optional(),
	speakerVoice: string().min(1).optional(),
	speakerVoiceId: string().min(1).optional(),
	voice: string().min(1).optional(),
	instructions: string().min(1).optional(),
	toolPolicy: DiscordVoiceRealtimeToolPolicySchema.optional(),
	consultPolicy: DiscordVoiceRealtimeConsultPolicySchema.optional(),
	requireWakeName: boolean().optional(),
	wakeNames: array(DiscordVoiceRealtimeWakeNameSchema).min(1).optional(),
	bootstrapContextFiles: array(DiscordVoiceRealtimeBootstrapContextFileSchema).optional(),
	bargeIn: boolean().optional(),
	minBargeInAudioEndMs: number().int().min(0).max(1e4).optional(),
	debounceMs: number().int().positive().max(1e4).optional(),
	providers: record(string(), record(string(), unknown()).optional()).optional()
}).strict();
const DiscordVoiceAgentSessionSchema = object({
	mode: _enum(["voice", "target"]).optional(),
	target: string().min(1).optional()
}).strict().superRefine((value, ctx) => {
	if (value.mode === "target" && !value.target) ctx.addIssue({
		code: ZodIssueCode.custom,
		path: ["target"],
		message: "voice.agentSession.target is required when mode is \"target\""
	});
});
const DiscordVoiceSchema = object({
	enabled: boolean().optional(),
	mode: _enum([
		"stt-tts",
		"agent-proxy",
		"bidi"
	]).optional(),
	agentSession: DiscordVoiceAgentSessionSchema.optional(),
	model: string().min(1).optional(),
	realtime: DiscordVoiceRealtimeSchema.optional(),
	autoJoin: array(DiscordVoiceAutoJoinSchema).optional(),
	followUsersEnabled: boolean().optional(),
	followUsers: array(string().min(1)).optional(),
	allowedChannels: array(DiscordVoiceAllowedChannelSchema).optional(),
	daveEncryption: boolean().optional(),
	decryptionFailureTolerance: number().int().min(0).optional(),
	connectTimeoutMs: number().int().positive().max(12e4).optional(),
	reconnectGraceMs: number().int().positive().max(12e4).optional(),
	captureSilenceGraceMs: number().int().positive().max(3e4).optional(),
	tts: TtsConfigSchema.optional()
}).strict().optional();
const DiscordAccountSchema = object({
	name: string().optional(),
	capabilities: array(string()).optional(),
	markdown: MarkdownConfigSchema,
	enabled: boolean().optional(),
	commands: ProviderCommandsSchema,
	configWrites: boolean().optional(),
	token: SecretInputSchema.optional().register(sensitive),
	applicationId: DiscordIdSchema.optional(),
	proxy: string().optional(),
	gatewayInfoTimeoutMs: number().int().positive().max(12e4).optional(),
	gatewayReadyTimeoutMs: number().int().positive().max(12e4).optional(),
	gatewayRuntimeReadyTimeoutMs: number().int().positive().max(12e4).optional(),
	allowBots: union([boolean(), literal("mentions")]).optional(),
	botLoopProtection: BotLoopProtectionSchema.optional(),
	dangerouslyAllowNameMatching: boolean().optional(),
	mentionAliases: record(string(), DiscordSnowflakeStringSchema).optional(),
	groupPolicy: GroupPolicySchema.optional().default("allowlist"),
	mentionPatterns: MentionPatternsPolicySchema.optional(),
	contextVisibility: ContextVisibilityModeSchema.optional(),
	historyLimit: number().int().min(0).optional(),
	dmHistoryLimit: number().int().min(0).optional(),
	dms: record(string(), DmConfigSchema.optional()).optional(),
	textChunkLimit: number().int().positive().optional(),
	suppressEmbeds: boolean().optional(),
	streaming: DiscordPreviewStreamingConfigSchema.optional(),
	maxLinesPerMessage: number().int().positive().optional(),
	mediaMaxMb: number().positive().optional(),
	retry: RetryConfigSchema,
	actions: object({
		reactions: boolean().optional(),
		stickers: boolean().optional(),
		emojiUploads: boolean().optional(),
		stickerUploads: boolean().optional(),
		polls: boolean().optional(),
		permissions: boolean().optional(),
		messages: boolean().optional(),
		threads: boolean().optional(),
		pins: boolean().optional(),
		search: boolean().optional(),
		memberInfo: boolean().optional(),
		roleInfo: boolean().optional(),
		roles: boolean().optional(),
		channelInfo: boolean().optional(),
		voiceStatus: boolean().optional(),
		events: boolean().optional(),
		moderation: boolean().optional(),
		channels: boolean().optional(),
		presence: boolean().optional()
	}).strict().optional(),
	replyToMode: ReplyToModeSchema.optional(),
	thread: DiscordThreadSchema.optional(),
	dmPolicy: DmPolicySchema.optional(),
	allowFrom: DiscordIdListSchema.optional(),
	defaultTo: string().optional(),
	dm: DiscordDmSchema.optional(),
	guilds: record(string(), DiscordGuildSchema.optional()).optional(),
	heartbeat: ChannelHeartbeatVisibilitySchema,
	healthMonitor: ChannelHealthMonitorSchema,
	execApprovals: object({
		enabled: NativeExecApprovalEnableModeSchema.optional(),
		approvers: DiscordIdListSchema.optional(),
		agentFilter: array(string()).optional(),
		sessionFilter: array(string()).optional(),
		cleanupAfterResolve: boolean().optional(),
		target: _enum([
			"dm",
			"channel",
			"both"
		]).optional()
	}).strict().optional(),
	agentComponents: object({
		enabled: boolean().optional(),
		ttlMs: number().int().positive().max(1440 * 60 * 1e3).optional()
	}).strict().optional(),
	ui: DiscordUiSchema,
	slashCommand: object({ ephemeral: boolean().optional() }).strict().optional(),
	threadBindings: object({
		enabled: boolean().optional(),
		idleHours: number().nonnegative().optional(),
		maxAgeHours: number().nonnegative().optional(),
		spawnSessions: boolean().optional(),
		defaultSpawnContext: _enum(["isolated", "fork"]).optional(),
		spawnSubagentSessions: boolean().optional(),
		spawnAcpSessions: boolean().optional()
	}).strict().optional(),
	intents: object({
		presence: boolean().optional(),
		guildMembers: boolean().optional(),
		voiceStates: boolean().optional()
	}).strict().optional(),
	voice: DiscordVoiceSchema,
	pluralkit: object({
		enabled: boolean().optional(),
		token: SecretInputSchema.optional().register(sensitive)
	}).strict().optional(),
	responsePrefix: string().optional(),
	ackReaction: string().optional(),
	ackReactionScope: _enum([
		"group-mentions",
		"group-all",
		"direct",
		"all",
		"off",
		"none"
	]).optional(),
	activity: string().optional(),
	status: _enum([
		"online",
		"dnd",
		"idle",
		"invisible"
	]).optional(),
	autoPresence: object({
		enabled: boolean().optional(),
		intervalMs: number().int().positive().optional(),
		minUpdateIntervalMs: number().int().positive().optional(),
		healthyText: string().optional(),
		degradedText: string().optional(),
		exhaustedText: string().optional()
	}).strict().optional(),
	activityType: union([
		literal(0),
		literal(1),
		literal(2),
		literal(3),
		literal(4),
		literal(5)
	]).optional(),
	activityUrl: string().url().optional(),
	inboundWorker: object({ runTimeoutMs: number().int().nonnegative().optional() }).strict().optional(),
	eventQueue: object({
		listenerTimeout: number().int().positive().optional(),
		maxQueueSize: number().int().positive().optional(),
		maxConcurrency: number().int().positive().optional()
	}).strict().optional()
}).strict().superRefine((value, ctx) => {
	const activityText = normalizeOptionalString(value.activity) ?? "";
	const hasActivity = Boolean(activityText);
	const hasActivityType = value.activityType !== void 0;
	const activityUrl = normalizeOptionalString(value.activityUrl) ?? "";
	const hasActivityUrl = Boolean(activityUrl);
	if ((hasActivityType || hasActivityUrl) && !hasActivity) ctx.addIssue({
		code: ZodIssueCode.custom,
		message: "channels.discord.activity is required when activityType or activityUrl is set",
		path: ["activity"]
	});
	if (value.activityType === 1 && !hasActivityUrl) ctx.addIssue({
		code: ZodIssueCode.custom,
		message: "channels.discord.activityUrl is required when activityType is 1 (Streaming)",
		path: ["activityUrl"]
	});
	if (hasActivityUrl && value.activityType !== 1) ctx.addIssue({
		code: ZodIssueCode.custom,
		message: "channels.discord.activityType must be 1 (Streaming) when activityUrl is set",
		path: ["activityType"]
	});
	const autoPresenceInterval = value.autoPresence?.intervalMs;
	const autoPresenceMinUpdate = value.autoPresence?.minUpdateIntervalMs;
	if (typeof autoPresenceInterval === "number" && typeof autoPresenceMinUpdate === "number" && autoPresenceMinUpdate > autoPresenceInterval) ctx.addIssue({
		code: ZodIssueCode.custom,
		message: "channels.discord.autoPresence.minUpdateIntervalMs must be less than or equal to channels.discord.autoPresence.intervalMs",
		path: ["autoPresence", "minUpdateIntervalMs"]
	});
});
const DiscordConfigSchema = DiscordAccountSchema.extend({
	accounts: record(string(), DiscordAccountSchema.optional()).optional(),
	defaultAccount: string().optional()
}).superRefine((value, ctx) => {
	const dmPolicy = value.dmPolicy ?? value.dm?.policy ?? "pairing";
	const allowFrom = value.allowFrom ?? value.dm?.allowFrom;
	const allowFromPath = value.allowFrom !== void 0 ? ["allowFrom"] : ["dm", "allowFrom"];
	requireOpenAllowFrom({
		policy: dmPolicy,
		allowFrom,
		ctx,
		path: [...allowFromPath],
		message: "channels.discord.dmPolicy=\"open\" requires channels.discord.allowFrom (or channels.discord.dm.allowFrom) to include \"*\""
	});
	requireAllowlistAllowFrom({
		policy: dmPolicy,
		allowFrom,
		ctx,
		path: [...allowFromPath],
		message: "channels.discord.dmPolicy=\"allowlist\" requires channels.discord.allowFrom (or channels.discord.dm.allowFrom) to contain at least one sender ID"
	});
	if (!value.accounts) return;
	for (const [accountId, account] of Object.entries(value.accounts)) {
		if (!account) continue;
		const effectivePolicy = account.dmPolicy ?? account.dm?.policy ?? value.dmPolicy ?? value.dm?.policy ?? "pairing";
		const effectiveAllowFrom = account.allowFrom ?? account.dm?.allowFrom ?? value.allowFrom ?? value.dm?.allowFrom;
		requireOpenAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.discord.accounts.*.dmPolicy=\"open\" requires channels.discord.accounts.*.allowFrom (or channels.discord.allowFrom) to include \"*\""
		});
		requireAllowlistAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.discord.accounts.*.dmPolicy=\"allowlist\" requires channels.discord.accounts.*.allowFrom (or channels.discord.allowFrom) to contain at least one sender ID"
		});
	}
});
const SlackDmSchema = object({
	enabled: boolean().optional(),
	policy: DmPolicySchema.optional(),
	allowFrom: array(union([string(), number()])).optional(),
	groupEnabled: boolean().optional(),
	groupChannels: array(union([string(), number()])).optional(),
	replyToMode: ReplyToModeSchema.optional()
}).strict();
const SlackChannelSchema = object({
	enabled: boolean().optional(),
	requireMention: boolean().optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema$1,
	allowBots: union([boolean(), literal("mentions")]).optional(),
	botLoopProtection: BotLoopProtectionSchema.optional(),
	users: array(union([string(), number()])).optional(),
	skills: array(string()).optional(),
	systemPrompt: string().optional()
}).strict();
const SlackThreadSchema = object({
	historyScope: _enum(["thread", "channel"]).optional(),
	inheritParent: boolean().optional(),
	initialHistoryLimit: number().int().min(0).optional(),
	requireExplicitMention: boolean().optional()
}).strict();
const SlackReplyToModeByChatTypeSchema = object({
	direct: ReplyToModeSchema.optional(),
	group: ReplyToModeSchema.optional(),
	channel: ReplyToModeSchema.optional()
}).strict();
const SlackSocketModeSchema = object({
	clientPingTimeout: number().int().positive().optional(),
	serverPingTimeout: number().int().positive().optional(),
	pingPongLoggingEnabled: boolean().optional()
}).strict();
const SlackAccountSchema = object({
	name: string().optional(),
	mode: _enum(["socket", "http"]).optional(),
	socketMode: SlackSocketModeSchema.optional(),
	signingSecret: SecretInputSchema.optional().register(sensitive),
	webhookPath: string().optional(),
	capabilities: SlackCapabilitiesSchema.optional(),
	execApprovals: object({
		enabled: NativeExecApprovalEnableModeSchema.optional(),
		approvers: array(union([string(), number()])).optional(),
		agentFilter: array(string()).optional(),
		sessionFilter: array(string()).optional(),
		target: _enum([
			"dm",
			"channel",
			"both"
		]).optional()
	}).strict().optional(),
	markdown: MarkdownConfigSchema,
	enabled: boolean().optional(),
	commands: ProviderCommandsSchema,
	configWrites: boolean().optional(),
	botToken: SecretInputSchema.optional().register(sensitive),
	appToken: SecretInputSchema.optional().register(sensitive),
	userToken: SecretInputSchema.optional().register(sensitive),
	userTokenReadOnly: boolean().optional().default(true),
	allowBots: union([boolean(), literal("mentions")]).optional(),
	botLoopProtection: BotLoopProtectionSchema.optional(),
	dangerouslyAllowNameMatching: boolean().optional(),
	requireMention: boolean().optional(),
	groupPolicy: GroupPolicySchema.optional(),
	mentionPatterns: MentionPatternsPolicySchema.optional(),
	contextVisibility: ContextVisibilityModeSchema.optional(),
	historyLimit: number().int().min(0).optional(),
	dmHistoryLimit: number().int().min(0).optional(),
	dms: record(string(), DmConfigSchema.optional()).optional(),
	textChunkLimit: number().int().positive().optional(),
	unfurlLinks: boolean().optional(),
	unfurlMedia: boolean().optional(),
	streaming: SlackStreamingConfigSchema.optional(),
	mediaMaxMb: number().positive().optional(),
	reactionNotifications: _enum([
		"off",
		"own",
		"all",
		"allowlist"
	]).optional(),
	reactionAllowlist: array(union([string(), number()])).optional(),
	replyToMode: ReplyToModeSchema.optional(),
	replyToModeByChatType: SlackReplyToModeByChatTypeSchema.optional(),
	thread: SlackThreadSchema.optional(),
	actions: object({
		reactions: boolean().optional(),
		messages: boolean().optional(),
		pins: boolean().optional(),
		search: boolean().optional(),
		permissions: boolean().optional(),
		memberInfo: boolean().optional(),
		channelInfo: boolean().optional(),
		emojiList: boolean().optional()
	}).strict().optional(),
	slashCommand: object({
		enabled: boolean().optional(),
		name: string().optional(),
		sessionPrefix: string().optional(),
		ephemeral: boolean().optional()
	}).strict().optional(),
	dmPolicy: DmPolicySchema.optional(),
	allowFrom: array(union([string(), number()])).optional(),
	defaultTo: string().optional(),
	dm: SlackDmSchema.optional(),
	channels: record(string(), SlackChannelSchema.optional()).optional(),
	heartbeat: ChannelHeartbeatVisibilitySchema,
	healthMonitor: ChannelHealthMonitorSchema,
	responsePrefix: string().optional(),
	ackReaction: string().optional(),
	typingReaction: string().optional()
}).strict().superRefine(() => {});
const SlackConfigSchema = SlackAccountSchema.safeExtend({
	mode: _enum(["socket", "http"]).optional().default("socket"),
	signingSecret: SecretInputSchema.optional().register(sensitive),
	webhookPath: string().optional().default("/slack/events"),
	groupPolicy: GroupPolicySchema.optional().default("allowlist"),
	mentionPatterns: MentionPatternsPolicySchema.optional(),
	contextVisibility: ContextVisibilityModeSchema.optional(),
	accounts: record(string(), SlackAccountSchema.optional()).optional(),
	defaultAccount: string().optional()
}).superRefine((value, ctx) => {
	const dmPolicy = value.dmPolicy ?? value.dm?.policy ?? "pairing";
	const allowFrom = value.allowFrom ?? value.dm?.allowFrom;
	const allowFromPath = value.allowFrom !== void 0 ? ["allowFrom"] : ["dm", "allowFrom"];
	requireOpenAllowFrom({
		policy: dmPolicy,
		allowFrom,
		ctx,
		path: [...allowFromPath],
		message: "channels.slack.dmPolicy=\"open\" requires channels.slack.allowFrom (or channels.slack.dm.allowFrom) to include \"*\""
	});
	requireAllowlistAllowFrom({
		policy: dmPolicy,
		allowFrom,
		ctx,
		path: [...allowFromPath],
		message: "channels.slack.dmPolicy=\"allowlist\" requires channels.slack.allowFrom (or channels.slack.dm.allowFrom) to contain at least one sender ID"
	});
	const baseMode = value.mode ?? "socket";
	if (!value.accounts) {
		validateSlackSigningSecretRequirements(value, ctx);
		return;
	}
	for (const [accountId, account] of Object.entries(value.accounts)) {
		if (!account) continue;
		if (account.enabled === false) continue;
		const accountMode = account.mode ?? baseMode;
		const effectivePolicy = account.dmPolicy ?? account.dm?.policy ?? value.dmPolicy ?? value.dm?.policy ?? "pairing";
		const effectiveAllowFrom = account.allowFrom ?? account.dm?.allowFrom ?? value.allowFrom ?? value.dm?.allowFrom;
		requireOpenAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.slack.accounts.*.dmPolicy=\"open\" requires channels.slack.accounts.*.allowFrom (or channels.slack.allowFrom) to include \"*\""
		});
		requireAllowlistAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.slack.accounts.*.dmPolicy=\"allowlist\" requires channels.slack.accounts.*.allowFrom (or channels.slack.allowFrom) to contain at least one sender ID"
		});
		if (accountMode !== "http") continue;
	}
	validateSlackSigningSecretRequirements(value, ctx);
});
const SignalGroupEntrySchema = object({
	requireMention: boolean().optional(),
	ingest: boolean().optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema$1
}).strict();
const SignalGroupsSchema = record(string(), SignalGroupEntrySchema.optional()).optional();
const SignalAccountSchemaBase = object({
	name: string().optional(),
	capabilities: array(string()).optional(),
	markdown: MarkdownConfigSchema,
	enabled: boolean().optional(),
	configWrites: boolean().optional(),
	account: string().optional(),
	accountUuid: string().optional(),
	configPath: string().optional(),
	httpUrl: string().optional(),
	httpHost: string().optional(),
	httpPort: number().int().positive().optional(),
	cliPath: ExecutableTokenSchema.optional(),
	autoStart: boolean().optional(),
	startupTimeoutMs: number().int().min(1e3).max(12e4).optional(),
	receiveMode: union([literal("on-start"), literal("manual")]).optional(),
	ignoreAttachments: boolean().optional(),
	ignoreStories: boolean().optional(),
	sendReadReceipts: boolean().optional(),
	dmPolicy: DmPolicySchema.optional().default("pairing"),
	allowFrom: array(union([string(), number()])).optional(),
	defaultTo: string().optional(),
	groupAllowFrom: array(union([string(), number()])).optional(),
	groupPolicy: GroupPolicySchema.optional().default("allowlist"),
	contextVisibility: ContextVisibilityModeSchema.optional(),
	groups: SignalGroupsSchema,
	historyLimit: number().int().min(0).optional(),
	dmHistoryLimit: number().int().min(0).optional(),
	dms: record(string(), DmConfigSchema.optional()).optional(),
	textChunkLimit: number().int().positive().optional(),
	chunkMode: _enum(["length", "newline"]).optional(),
	blockStreaming: boolean().optional(),
	blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
	mediaMaxMb: number().int().positive().optional(),
	reactionNotifications: _enum([
		"off",
		"own",
		"all",
		"allowlist"
	]).optional(),
	reactionAllowlist: array(union([string(), number()])).optional(),
	actions: object({ reactions: boolean().optional() }).strict().optional(),
	reactionLevel: _enum([
		"off",
		"ack",
		"minimal",
		"extensive"
	]).optional(),
	heartbeat: ChannelHeartbeatVisibilitySchema,
	healthMonitor: ChannelHealthMonitorSchema,
	responsePrefix: string().optional()
}).strict();
const SignalAccountSchema = SignalAccountSchemaBase;
const SignalConfigSchema = SignalAccountSchemaBase.extend({
	apiMode: _enum([
		"auto",
		"native",
		"container"
	]).optional(),
	accounts: record(string(), SignalAccountSchema.optional()).optional(),
	defaultAccount: string().optional()
}).superRefine((value, ctx) => {
	requireOpenAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.signal.dmPolicy=\"open\" requires channels.signal.allowFrom to include \"*\""
	});
	requireAllowlistAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.signal.dmPolicy=\"allowlist\" requires channels.signal.allowFrom to contain at least one sender ID"
	});
	if (!value.accounts) return;
	for (const [accountId, account] of Object.entries(value.accounts)) {
		if (!account) continue;
		const effectivePolicy = account.dmPolicy ?? value.dmPolicy;
		const effectiveAllowFrom = account.allowFrom ?? value.allowFrom;
		requireOpenAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.signal.accounts.*.dmPolicy=\"open\" requires channels.signal.accounts.*.allowFrom (or channels.signal.allowFrom) to include \"*\""
		});
		requireAllowlistAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.signal.accounts.*.dmPolicy=\"allowlist\" requires channels.signal.accounts.*.allowFrom (or channels.signal.allowFrom) to contain at least one sender ID"
		});
	}
});
const IrcGroupSchema = object({
	requireMention: boolean().optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema$1,
	skills: array(string()).optional(),
	enabled: boolean().optional(),
	allowFrom: array(union([string(), number()])).optional(),
	systemPrompt: string().optional()
}).strict();
const IrcNickServSchema = object({
	enabled: boolean().optional(),
	service: string().optional(),
	password: SecretInputSchema.optional().register(sensitive),
	passwordFile: string().optional(),
	register: boolean().optional(),
	registerEmail: string().optional()
}).strict();
const IrcAccountSchemaBase = object({
	name: string().optional(),
	capabilities: array(string()).optional(),
	markdown: MarkdownConfigSchema,
	enabled: boolean().optional(),
	configWrites: boolean().optional(),
	host: string().optional(),
	port: number().int().min(1).max(65535).optional(),
	tls: boolean().optional(),
	nick: string().optional(),
	username: string().optional(),
	realname: string().optional(),
	password: SecretInputSchema.optional().register(sensitive),
	passwordFile: string().optional(),
	nickserv: IrcNickServSchema.optional(),
	channels: array(string()).optional(),
	dmPolicy: DmPolicySchema.optional().default("pairing"),
	allowFrom: array(union([string(), number()])).optional(),
	defaultTo: string().optional(),
	groupAllowFrom: array(union([string(), number()])).optional(),
	groupPolicy: GroupPolicySchema.optional().default("allowlist"),
	contextVisibility: ContextVisibilityModeSchema.optional(),
	groups: record(string(), IrcGroupSchema.optional()).optional(),
	mentionPatterns: array(string()).optional(),
	historyLimit: number().int().min(0).optional(),
	dmHistoryLimit: number().int().min(0).optional(),
	dms: record(string(), DmConfigSchema.optional()).optional(),
	textChunkLimit: number().int().positive().optional(),
	chunkMode: _enum(["length", "newline"]).optional(),
	blockStreaming: boolean().optional(),
	blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
	mediaMaxMb: number().positive().optional(),
	heartbeat: ChannelHeartbeatVisibilitySchema,
	healthMonitor: ChannelHealthMonitorSchema,
	responsePrefix: string().optional()
}).strict();
function refineIrcAllowFromAndNickserv(value, ctx) {
	requireOpenAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.irc.dmPolicy=\"open\" requires channels.irc.allowFrom to include \"*\""
	});
	requireAllowlistAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.irc.dmPolicy=\"allowlist\" requires channels.irc.allowFrom to contain at least one sender ID"
	});
	if (value.nickserv?.register && !value.nickserv.registerEmail?.trim()) ctx.addIssue({
		code: ZodIssueCode.custom,
		path: ["nickserv", "registerEmail"],
		message: "channels.irc.nickserv.register=true requires channels.irc.nickserv.registerEmail"
	});
}
const IrcAccountSchema = IrcAccountSchemaBase.superRefine((value, ctx) => {
	if (value.nickserv?.register && !value.nickserv.registerEmail?.trim()) ctx.addIssue({
		code: ZodIssueCode.custom,
		path: ["nickserv", "registerEmail"],
		message: "channels.irc.nickserv.register=true requires channels.irc.nickserv.registerEmail"
	});
});
IrcAccountSchemaBase.extend({
	accounts: record(string(), IrcAccountSchema.optional()).optional(),
	defaultAccount: string().optional()
}).superRefine((value, ctx) => {
	refineIrcAllowFromAndNickserv(value, ctx);
	if (!value.accounts) return;
	for (const [accountId, account] of Object.entries(value.accounts)) {
		if (!account) continue;
		const effectivePolicy = account.dmPolicy ?? value.dmPolicy;
		const effectiveAllowFrom = account.allowFrom ?? value.allowFrom;
		requireOpenAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.irc.accounts.*.dmPolicy=\"open\" requires channels.irc.accounts.*.allowFrom (or channels.irc.allowFrom) to include \"*\""
		});
		requireAllowlistAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.irc.accounts.*.dmPolicy=\"allowlist\" requires channels.irc.accounts.*.allowFrom (or channels.irc.allowFrom) to contain at least one sender ID"
		});
	}
});
const IMessageActionSchema = object({
	reactions: boolean().optional(),
	edit: boolean().optional(),
	unsend: boolean().optional(),
	reply: boolean().optional(),
	sendWithEffect: boolean().optional(),
	renameGroup: boolean().optional(),
	setGroupIcon: boolean().optional(),
	addParticipant: boolean().optional(),
	removeParticipant: boolean().optional(),
	leaveGroup: boolean().optional(),
	sendAttachment: boolean().optional()
}).strict().optional();
const IMessageAccountSchemaBase = object({
	name: string().optional(),
	capabilities: array(string()).optional(),
	markdown: MarkdownConfigSchema,
	enabled: boolean().optional(),
	configWrites: boolean().optional(),
	cliPath: ExecutableTokenSchema.optional(),
	dbPath: string().optional(),
	remoteHost: string().refine(isSafeScpRemoteHost, "expected SSH host or user@host (no spaces/options)").optional(),
	actions: IMessageActionSchema,
	service: union([
		literal("imessage"),
		literal("sms"),
		literal("auto")
	]).optional(),
	sendTransport: _enum([
		"auto",
		"bridge",
		"applescript"
	]).optional(),
	region: string().optional(),
	dmPolicy: DmPolicySchema.optional().default("pairing"),
	allowFrom: array(union([string(), number()])).optional(),
	defaultTo: string().optional(),
	groupAllowFrom: array(union([string(), number()])).optional(),
	groupPolicy: GroupPolicySchema.optional().default("allowlist"),
	contextVisibility: ContextVisibilityModeSchema.optional(),
	historyLimit: number().int().min(0).optional(),
	dmHistoryLimit: number().int().min(0).optional(),
	dms: record(string(), DmConfigSchema.optional()).optional(),
	includeAttachments: boolean().optional(),
	attachmentRoots: array(string().refine(isValidInboundPathRootPattern, "expected absolute path root")).optional(),
	remoteAttachmentRoots: array(string().refine(isValidInboundPathRootPattern, "expected absolute path root")).optional(),
	mediaMaxMb: number().int().positive().optional(),
	probeTimeoutMs: number().int().positive().optional(),
	textChunkLimit: number().int().positive().optional(),
	chunkMode: _enum(["length", "newline"]).optional(),
	streaming: ChannelDeliveryStreamingConfigSchema.optional(),
	blockStreaming: boolean().optional(),
	blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
	sendReadReceipts: boolean().optional(),
	reactionNotifications: _enum([
		"off",
		"own",
		"all"
	]).optional(),
	coalesceSameSenderDms: boolean().optional(),
	catchup: object({
		enabled: boolean().optional(),
		maxAgeMinutes: number().int().min(1).max(720).optional(),
		perRunLimit: number().int().min(1).max(500).optional(),
		firstRunLookbackMinutes: number().int().min(1).max(720).optional(),
		maxFailureRetries: number().int().min(1).max(1e3).optional()
	}).strict().optional(),
	groups: record(string(), object({
		requireMention: boolean().optional(),
		tools: ToolPolicySchema,
		toolsBySender: ToolPolicyBySenderSchema$1,
		systemPrompt: string().optional()
	}).strict().optional()).optional(),
	heartbeat: ChannelHeartbeatVisibilitySchema,
	healthMonitor: ChannelHealthMonitorSchema,
	responsePrefix: string().optional()
}).strict();
const IMessageAccountSchema = IMessageAccountSchemaBase;
const IMessageConfigSchema = IMessageAccountSchemaBase.extend({
	accounts: record(string(), IMessageAccountSchema.optional()).optional(),
	defaultAccount: string().optional()
}).superRefine((value, ctx) => {
	requireOpenAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.imessage.dmPolicy=\"open\" requires channels.imessage.allowFrom to include \"*\""
	});
	requireAllowlistAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.imessage.dmPolicy=\"allowlist\" requires channels.imessage.allowFrom to contain at least one sender ID"
	});
	if (!value.accounts) return;
	for (const [accountId, account] of Object.entries(value.accounts)) {
		if (!account) continue;
		const effectivePolicy = account.dmPolicy ?? value.dmPolicy;
		const effectiveAllowFrom = account.allowFrom ?? value.allowFrom;
		requireOpenAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.imessage.accounts.*.dmPolicy=\"open\" requires channels.imessage.accounts.*.allowFrom (or channels.imessage.allowFrom) to include \"*\""
		});
		requireAllowlistAllowFrom({
			policy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.imessage.accounts.*.dmPolicy=\"allowlist\" requires channels.imessage.accounts.*.allowFrom (or channels.imessage.allowFrom) to contain at least one sender ID"
		});
	}
});
const MSTeamsChannelSchema = object({
	requireMention: boolean().optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema$1,
	replyStyle: MSTeamsReplyStyleSchema.optional()
}).strict();
const MSTeamsTeamSchema = object({
	requireMention: boolean().optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema$1,
	replyStyle: MSTeamsReplyStyleSchema.optional(),
	channels: record(string(), MSTeamsChannelSchema.optional()).optional()
}).strict();
const MSTEAMS_SERVICE_URL_HOST_ALLOWLIST = [
	"smba.trafficmanager.net",
	"smba.infra.gcc.teams.microsoft.com",
	"smba.infra.gov.teams.microsoft.us",
	"smba.infra.dod.teams.microsoft.us",
	"botframework.azure.cn"
];
function isAllowedMSTeamsServiceUrl(value) {
	try {
		const parsed = new URL(value.trim());
		if (parsed.protocol !== "https:") return false;
		const host = parsed.hostname.toLowerCase();
		return MSTEAMS_SERVICE_URL_HOST_ALLOWLIST.some((allowed) => host === allowed || host.endsWith(`.${allowed}`));
	} catch {
		return false;
	}
}
function isAzureChinaBotFrameworkServiceUrl(value) {
	try {
		const parsed = new URL(value.trim());
		if (parsed.protocol !== "https:") return false;
		const host = parsed.hostname.toLowerCase();
		return host === "botframework.azure.cn" || host.endsWith(".botframework.azure.cn");
	} catch {
		return false;
	}
}
const MSTeamsConfigSchema = object({
	enabled: boolean().optional(),
	capabilities: array(string()).optional(),
	dangerouslyAllowNameMatching: boolean().optional(),
	markdown: MarkdownConfigSchema,
	configWrites: boolean().optional(),
	appId: string().optional(),
	appPassword: SecretInputSchema.optional().register(sensitive),
	tenantId: string().optional(),
	cloud: _enum([
		"Public",
		"USGov",
		"USGovDoD",
		"China"
	]).optional(),
	serviceUrl: string().url().refine(isAllowedMSTeamsServiceUrl, { message: "channels.msteams.serviceUrl must use a supported Microsoft Teams Bot Connector host" }).optional(),
	authType: _enum(["secret", "federated"]).optional(),
	certificatePath: string().optional(),
	certificateThumbprint: string().optional(),
	useManagedIdentity: boolean().optional(),
	managedIdentityClientId: string().optional(),
	webhook: object({
		port: number().int().positive().optional(),
		path: string().optional()
	}).strict().optional(),
	dmPolicy: DmPolicySchema.optional().default("pairing"),
	allowFrom: array(string()).optional(),
	defaultTo: string().optional(),
	groupAllowFrom: array(string()).optional(),
	groupPolicy: GroupPolicySchema.optional().default("allowlist"),
	contextVisibility: ContextVisibilityModeSchema.optional(),
	textChunkLimit: number().int().positive().optional(),
	chunkMode: _enum(["length", "newline"]).optional(),
	streaming: ChannelPreviewStreamingConfigSchema.optional(),
	typingIndicator: boolean().optional(),
	blockStreaming: boolean().optional(),
	blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
	mediaAllowHosts: array(string()).optional(),
	mediaAuthAllowHosts: array(string()).optional(),
	requireMention: boolean().optional(),
	historyLimit: number().int().min(0).optional(),
	dmHistoryLimit: number().int().min(0).optional(),
	dms: record(string(), DmConfigSchema.optional()).optional(),
	replyStyle: MSTeamsReplyStyleSchema.optional(),
	teams: record(string(), MSTeamsTeamSchema.optional()).optional(),
	/** Max media size in MB (default: 100MB for OneDrive upload support). */
	mediaMaxMb: number().positive().optional(),
	/** SharePoint site ID for file uploads in group chats/channels (e.g., "contoso.sharepoint.com,guid1,guid2") */
	sharePointSiteId: string().optional(),
	heartbeat: ChannelHeartbeatVisibilitySchema,
	healthMonitor: ChannelHealthMonitorSchema,
	responsePrefix: string().optional(),
	welcomeCard: boolean().optional(),
	promptStarters: array(string()).optional(),
	groupWelcomeCard: boolean().optional(),
	feedbackEnabled: boolean().optional(),
	feedbackReflection: boolean().optional(),
	feedbackReflectionCooldownMs: number().int().min(0).optional(),
	delegatedAuth: object({
		enabled: boolean().optional(),
		scopes: array(string()).optional()
	}).strict().optional(),
	sso: object({
		enabled: boolean().optional(),
		connectionName: string().optional()
	}).strict().optional()
}).strict().superRefine((value, ctx) => {
	requireOpenAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.msteams.dmPolicy=\"open\" requires channels.msteams.allowFrom to include \"*\""
	});
	requireAllowlistAllowFrom({
		policy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.msteams.dmPolicy=\"allowlist\" requires channels.msteams.allowFrom to contain at least one sender ID"
	});
	if (value.sso?.enabled === true && !value.sso.connectionName?.trim()) ctx.addIssue({
		code: ZodIssueCode.custom,
		path: ["sso", "connectionName"],
		message: "channels.msteams.sso.enabled=true requires channels.msteams.sso.connectionName to identify the Bot Framework OAuth connection"
	});
	if (value.cloud && value.cloud !== "Public" && value.cloud !== "China" && !value.serviceUrl?.trim()) ctx.addIssue({
		code: ZodIssueCode.custom,
		path: ["serviceUrl"],
		message: "channels.msteams.cloud requires channels.msteams.serviceUrl for non-public Teams clouds"
	});
	if (value.cloud === "China" && value.serviceUrl?.trim() && !isAzureChinaBotFrameworkServiceUrl(value.serviceUrl)) ctx.addIssue({
		code: ZodIssueCode.custom,
		path: ["serviceUrl"],
		message: "channels.msteams.cloud=China requires channels.msteams.serviceUrl to use an Azure China Bot Framework channel host"
	});
	if (value.cloud !== "China" && value.serviceUrl?.trim() && isAzureChinaBotFrameworkServiceUrl(value.serviceUrl)) ctx.addIssue({
		code: ZodIssueCode.custom,
		path: ["cloud"],
		message: "Azure China Bot Framework serviceUrl hosts require channels.msteams.cloud=China"
	});
});
//#endregion
//#region src/config/zod-schema.providers-googlechat.ts
/** DM policy schema for Google Chat accounts. */
const GoogleChatDmSchema = object({
	enabled: boolean().optional(),
	policy: DmPolicySchema.optional().default("pairing"),
	allowFrom: array(union([string(), number()])).optional()
}).strict().superRefine((value, ctx) => {
	requireOpenAllowFrom({
		policy: value.policy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.googlechat.dm.policy=\"open\" requires channels.googlechat.dm.allowFrom to include \"*\""
	});
	requireAllowlistAllowFrom({
		policy: value.policy,
		allowFrom: value.allowFrom,
		ctx,
		path: ["allowFrom"],
		message: "channels.googlechat.dm.policy=\"allowlist\" requires channels.googlechat.dm.allowFrom to contain at least one sender ID"
	});
});
const GoogleChatGroupSchema = object({
	enabled: boolean().optional(),
	requireMention: boolean().optional(),
	botLoopProtection: ChannelBotLoopProtectionSchema.optional(),
	users: array(union([string(), number()])).optional(),
	systemPrompt: string().optional()
}).strict();
const GoogleChatAccountSchema = object({
	name: string().optional(),
	capabilities: array(string()).optional(),
	enabled: boolean().optional(),
	configWrites: boolean().optional(),
	allowBots: boolean().optional(),
	botLoopProtection: ChannelBotLoopProtectionSchema.optional(),
	dangerouslyAllowNameMatching: boolean().optional(),
	requireMention: boolean().optional(),
	groupPolicy: GroupPolicySchema.optional().default("allowlist"),
	groupAllowFrom: array(union([string(), number()])).optional(),
	groups: record(string(), GoogleChatGroupSchema.optional()).optional(),
	defaultTo: string().optional(),
	serviceAccount: union([
		string(),
		record(string(), unknown()),
		SecretRefSchema
	]).optional().register(sensitive),
	serviceAccountRef: SecretRefSchema.optional().register(sensitive),
	serviceAccountFile: string().optional(),
	audienceType: _enum(["app-url", "project-number"]).optional(),
	audience: string().optional(),
	appPrincipal: string().optional(),
	webhookPath: string().optional(),
	webhookUrl: string().optional(),
	botUser: string().optional(),
	historyLimit: number().int().min(0).optional(),
	dmHistoryLimit: number().int().min(0).optional(),
	dms: record(string(), DmConfigSchema.optional()).optional(),
	textChunkLimit: number().int().positive().optional(),
	chunkMode: _enum(["length", "newline"]).optional(),
	blockStreaming: boolean().optional(),
	blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
	mediaMaxMb: number().positive().optional(),
	replyToMode: ReplyToModeSchema.optional(),
	actions: object({ reactions: boolean().optional() }).strict().optional(),
	dm: GoogleChatDmSchema.optional(),
	healthMonitor: ChannelHealthMonitorSchema,
	typingIndicator: _enum([
		"none",
		"message",
		"reaction"
	]).optional(),
	responsePrefix: string().optional()
}).strict();
const GoogleChatConfigSchema = GoogleChatAccountSchema.extend({
	accounts: record(string(), GoogleChatAccountSchema.optional()).optional(),
	defaultAccount: string().optional()
});
//#endregion
//#region src/config/zod-schema.providers-whatsapp.ts
const ToolPolicyBySenderSchema = record(string(), ToolPolicySchema).optional();
const WhatsAppGroupEntrySchema = object({
	requireMention: boolean().optional(),
	tools: ToolPolicySchema,
	toolsBySender: ToolPolicyBySenderSchema,
	systemPrompt: string().optional()
}).strict().optional();
const WhatsAppGroupsSchema = record(string(), WhatsAppGroupEntrySchema).optional();
const WhatsAppDirectEntrySchema = object({ systemPrompt: string().optional() }).strict().optional();
const WhatsAppDirectSchema = record(string(), WhatsAppDirectEntrySchema).optional();
const WhatsAppAckReactionSchema = object({
	emoji: string().optional(),
	direct: boolean().optional().default(true),
	group: _enum([
		"always",
		"mentions",
		"never"
	]).optional().default("mentions")
}).strict().optional();
const WhatsAppPluginHooksSchema = object({ messageReceived: boolean().optional() }).strict().optional();
function stripDeprecatedWhatsAppNoopKeys(value) {
	if (!value || typeof value !== "object" || Array.isArray(value)) return value;
	if (!Object.hasOwn(value, "exposeErrorText")) return value;
	const next = { ...value };
	delete next.exposeErrorText;
	return next;
}
function buildWhatsAppCommonShape(params) {
	return {
		enabled: boolean().optional(),
		capabilities: array(string()).optional(),
		markdown: MarkdownConfigSchema,
		configWrites: boolean().optional(),
		sendReadReceipts: boolean().optional(),
		messagePrefix: string().optional(),
		responsePrefix: string().optional(),
		dmPolicy: params.useDefaults ? DmPolicySchema.optional().default("pairing") : DmPolicySchema.optional(),
		selfChatMode: boolean().optional(),
		allowFrom: array(string()).optional(),
		defaultTo: string().optional(),
		groupAllowFrom: array(string()).optional(),
		groupPolicy: params.useDefaults ? GroupPolicySchema.optional().default("allowlist") : GroupPolicySchema.optional(),
		mentionPatterns: MentionPatternsPolicySchema.optional(),
		contextVisibility: ContextVisibilityModeSchema.optional(),
		historyLimit: number().int().min(0).optional(),
		dmHistoryLimit: number().int().min(0).optional(),
		dms: record(string(), DmConfigSchema.optional()).optional(),
		textChunkLimit: number().int().positive().optional(),
		chunkMode: _enum(["length", "newline"]).optional(),
		blockStreaming: boolean().optional(),
		blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
		groups: WhatsAppGroupsSchema,
		direct: WhatsAppDirectSchema,
		ackReaction: WhatsAppAckReactionSchema,
		reactionLevel: _enum([
			"off",
			"ack",
			"minimal",
			"extensive"
		]).optional(),
		debounceMs: params.useDefaults ? number().int().nonnegative().optional().default(0) : number().int().nonnegative().optional(),
		replyToMode: ReplyToModeSchema.optional(),
		heartbeat: ChannelHeartbeatVisibilitySchema,
		healthMonitor: ChannelHealthMonitorSchema,
		pluginHooks: WhatsAppPluginHooksSchema
	};
}
function enforceOpenDmPolicyAllowFromStar(params) {
	if (params.dmPolicy !== "open") return;
	if (normalizeStringEntries(Array.isArray(params.allowFrom) ? params.allowFrom : []).includes("*")) return;
	params.ctx.addIssue({
		code: ZodIssueCode.custom,
		path: params.path ?? ["allowFrom"],
		message: params.message
	});
}
function enforceAllowlistDmPolicyAllowFrom(params) {
	if (params.dmPolicy !== "allowlist") return;
	if (normalizeStringEntries(Array.isArray(params.allowFrom) ? params.allowFrom : []).length > 0) return;
	params.ctx.addIssue({
		code: ZodIssueCode.custom,
		path: params.path ?? ["allowFrom"],
		message: params.message
	});
}
const WhatsAppAccountSchema = preprocess(stripDeprecatedWhatsAppNoopKeys, object({
	...buildWhatsAppCommonShape({ useDefaults: false }),
	name: string().optional(),
	enabled: boolean().optional(),
	/** Override auth directory for this WhatsApp account (Baileys multi-file auth state). */
	authDir: string().optional(),
	mediaMaxMb: number().int().positive().optional()
}).strict());
const WhatsAppConfigSchema = preprocess(stripDeprecatedWhatsAppNoopKeys, object({
	...buildWhatsAppCommonShape({ useDefaults: true }),
	accounts: record(string(), WhatsAppAccountSchema.optional()).optional(),
	defaultAccount: string().optional(),
	mediaMaxMb: number().int().positive().optional().default(50),
	actions: object({
		reactions: boolean().optional(),
		sendMessage: boolean().optional(),
		polls: boolean().optional()
	}).strict().optional()
}).strict().superRefine((value, ctx) => {
	const defaultAccount = resolveAccountEntry(value.accounts, "default");
	enforceOpenDmPolicyAllowFromStar({
		dmPolicy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		message: "channels.whatsapp.dmPolicy=\"open\" requires channels.whatsapp.allowFrom to include \"*\""
	});
	enforceAllowlistDmPolicyAllowFrom({
		dmPolicy: value.dmPolicy,
		allowFrom: value.allowFrom,
		ctx,
		message: "channels.whatsapp.dmPolicy=\"allowlist\" requires channels.whatsapp.allowFrom to contain at least one sender ID"
	});
	if (!value.accounts) return;
	for (const [accountId, account] of Object.entries(value.accounts)) {
		if (!account) continue;
		const effectivePolicy = account.dmPolicy ?? (accountId === "default" ? void 0 : defaultAccount?.dmPolicy) ?? value.dmPolicy;
		const effectiveAllowFrom = account.allowFrom ?? (accountId === "default" ? void 0 : defaultAccount?.allowFrom) ?? value.allowFrom;
		enforceOpenDmPolicyAllowFromStar({
			dmPolicy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.whatsapp.accounts.*.dmPolicy=\"open\" requires channels.whatsapp.accounts.*.allowFrom (or channels.whatsapp.allowFrom) to include \"*\""
		});
		enforceAllowlistDmPolicyAllowFrom({
			dmPolicy: effectivePolicy,
			allowFrom: effectiveAllowFrom,
			ctx,
			path: [
				"accounts",
				accountId,
				"allowFrom"
			],
			message: "channels.whatsapp.accounts.*.dmPolicy=\"allowlist\" requires channels.whatsapp.accounts.*.allowFrom (or channels.whatsapp.allowFrom) to contain at least one sender ID"
		});
	}
}));
//#endregion
export { MSTeamsConfigSchema as a, TelegramConfigSchema as c, IMessageConfigSchema as i, GoogleChatConfigSchema as n, SignalConfigSchema as o, DiscordConfigSchema as r, SlackConfigSchema as s, WhatsAppConfigSchema as t };