@gguf/claw
Version:
WhatsApp gateway CLI (Baileys web) with Pi RPC agent
1,502 lines (1,488 loc) • 808 kB
JavaScript
import "./pi-model-discovery-Dw3A6oXH.js";
import { createRequire } from "node:module";
import { z } from "zod";
import os, { homedir } from "node:os";
import path from "node:path";
import fs, { createWriteStream, existsSync, statSync } from "node:fs";
import { Logger } from "tslog";
import json5 from "json5";
import chalk, { Chalk } from "chalk";
import fs$1 from "node:fs/promises";
import { execFile, execFileSync, spawn } from "node:child_process";
import { promisify } from "node:util";
import { fileURLToPath } from "node:url";
import crypto, { X509Certificate, randomUUID } from "node:crypto";
import "proper-lockfile";
import { getEnvApiKey, getOAuthProviders } from "@mariozechner/pi-ai";
import { BedrockClient, ListFoundationModelsCommand } from "@aws-sdk/client-bedrock";
import AjvPkg from "ajv";
import "@mariozechner/pi-coding-agent";
import { Type } from "@sinclair/typebox";
import { fileTypeFromBuffer } from "file-type";
import { request } from "node:https";
import { pipeline } from "node:stream/promises";
import { lookup } from "node:dns";
import { lookup as lookup$1 } from "node:dns/promises";
import { Agent } from "undici";
import { PermissionFlagsBits } from "discord-api-types/v10";
import { RequestClient } from "@buape/carbon";
import "discord-api-types/payloads/v10";
import "markdown-it";
import "@clack/prompts";
import { WebSocket } from "ws";
import { Buffer as Buffer$1 } from "node:buffer";
import { WebClient } from "@slack/web-api";
import "yaml";
import "express";
import "node-edge-tts";
import "jiti";
import "chokidar";
import { Bot } from "grammy";
import "osc-progress";
import "@buape/carbon/gateway";
import { messagingApi } from "@line/bot-sdk";
import SlackBolt from "@slack/bolt";
import "@grammyjs/runner";
import "@grammyjs/transformer-throttler";
import { DisconnectReason, fetchLatestBaileysVersion, makeCacheableSignalKeyStore, makeWASocket, useMultiFileAuthState } from "@whiskeysockets/baileys";
import "qrcode-terminal/vendor/QRCode/index.js";
import "qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js";
import qrcode from "qrcode-terminal";
import { EventEmitter } from "node:events";
//#region src/channels/plugins/message-action-names.ts
const CHANNEL_MESSAGE_ACTION_NAMES = [
"send",
"broadcast",
"poll",
"react",
"reactions",
"read",
"edit",
"unsend",
"reply",
"sendWithEffect",
"renameGroup",
"setGroupIcon",
"addParticipant",
"removeParticipant",
"leaveGroup",
"sendAttachment",
"delete",
"pin",
"unpin",
"list-pins",
"permissions",
"thread-create",
"thread-list",
"thread-reply",
"search",
"sticker",
"sticker-search",
"member-info",
"role-info",
"emoji-list",
"emoji-upload",
"sticker-upload",
"role-add",
"role-remove",
"channel-info",
"channel-list",
"channel-create",
"channel-edit",
"channel-delete",
"channel-move",
"category-create",
"category-edit",
"category-delete",
"voice-status",
"event-list",
"event-create",
"timeout",
"kick",
"ban",
"set-presence"
];
//#endregion
//#region src/channels/plugins/bluebubbles-actions.ts
const BLUEBUBBLES_ACTIONS = {
react: { gate: "reactions" },
edit: {
gate: "edit",
unsupportedOnMacOS26: true
},
unsend: { gate: "unsend" },
reply: { gate: "reply" },
sendWithEffect: { gate: "sendWithEffect" },
renameGroup: {
gate: "renameGroup",
groupOnly: true
},
setGroupIcon: {
gate: "setGroupIcon",
groupOnly: true
},
addParticipant: {
gate: "addParticipant",
groupOnly: true
},
removeParticipant: {
gate: "removeParticipant",
groupOnly: true
},
leaveGroup: {
gate: "leaveGroup",
groupOnly: true
},
sendAttachment: { gate: "sendAttachment" }
};
const BLUEBUBBLES_ACTION_SPECS = BLUEBUBBLES_ACTIONS;
const BLUEBUBBLES_ACTION_NAMES = Object.keys(BLUEBUBBLES_ACTIONS);
const BLUEBUBBLES_GROUP_ACTIONS = new Set(BLUEBUBBLES_ACTION_NAMES.filter((action) => BLUEBUBBLES_ACTION_SPECS[action]?.groupOnly));
//#endregion
//#region src/plugins/http-path.ts
function normalizePluginHttpPath(path, fallback) {
const trimmed = path?.trim();
if (!trimmed) {
const fallbackTrimmed = fallback?.trim();
if (!fallbackTrimmed) return null;
return fallbackTrimmed.startsWith("/") ? fallbackTrimmed : `/${fallbackTrimmed}`;
}
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
}
//#endregion
//#region src/plugins/runtime.ts
const createEmptyRegistry = () => ({
plugins: [],
tools: [],
hooks: [],
typedHooks: [],
channels: [],
providers: [],
gatewayHandlers: {},
httpHandlers: [],
httpRoutes: [],
cliRegistrars: [],
services: [],
commands: [],
diagnostics: []
});
const REGISTRY_STATE = Symbol.for("openclaw.pluginRegistryState");
const state = (() => {
const globalState = globalThis;
if (!globalState[REGISTRY_STATE]) globalState[REGISTRY_STATE] = {
registry: createEmptyRegistry(),
key: null
};
return globalState[REGISTRY_STATE];
})();
function getActivePluginRegistry() {
return state.registry;
}
function requireActivePluginRegistry() {
if (!state.registry) state.registry = createEmptyRegistry();
return state.registry;
}
//#endregion
//#region src/plugins/http-registry.ts
function registerPluginHttpRoute(params) {
const registry = params.registry ?? requireActivePluginRegistry();
const routes = registry.httpRoutes ?? [];
registry.httpRoutes = routes;
const normalizedPath = normalizePluginHttpPath(params.path, params.fallbackPath);
const suffix = params.accountId ? ` for account "${params.accountId}"` : "";
if (!normalizedPath) {
params.log?.(`plugin: webhook path missing${suffix}`);
return () => {};
}
if (routes.some((entry) => entry.path === normalizedPath)) {
const pluginHint = params.pluginId ? ` (${params.pluginId})` : "";
params.log?.(`plugin: webhook path ${normalizedPath} already registered${suffix}${pluginHint}`);
return () => {};
}
const entry = {
path: normalizedPath,
handler: params.handler,
pluginId: params.pluginId,
source: params.source
};
routes.push(entry);
return () => {
const index = routes.indexOf(entry);
if (index >= 0) routes.splice(index, 1);
};
}
//#endregion
//#region src/plugins/config-schema.ts
function error(message) {
return {
success: false,
error: { issues: [{
path: [],
message
}] }
};
}
function emptyPluginConfigSchema() {
return {
safeParse(value) {
if (value === void 0) return {
success: true,
data: void 0
};
if (!value || typeof value !== "object" || Array.isArray(value)) return error("expected config object");
if (Object.keys(value).length > 0) return error("config must be empty");
return {
success: true,
data: value
};
},
jsonSchema: {
type: "object",
additionalProperties: false,
properties: {}
}
};
}
//#endregion
//#region src/channels/registry.ts
const CHAT_CHANNEL_ORDER = [
"telegram",
"whatsapp",
"discord",
"googlechat",
"slack",
"signal",
"imessage"
];
const CHANNEL_IDS = [...CHAT_CHANNEL_ORDER];
const CHAT_CHANNEL_META = {
telegram: {
id: "telegram",
label: "Telegram",
selectionLabel: "Telegram (Bot API)",
detailLabel: "Telegram Bot",
docsPath: "/channels/telegram",
docsLabel: "telegram",
blurb: "simplest way to get started — register a bot with @BotFather and get going.",
systemImage: "paperplane",
selectionDocsPrefix: "",
selectionDocsOmitLabel: true,
selectionExtras: ["https://openclaw.ai"]
},
whatsapp: {
id: "whatsapp",
label: "WhatsApp",
selectionLabel: "WhatsApp (QR link)",
detailLabel: "WhatsApp Web",
docsPath: "/channels/whatsapp",
docsLabel: "whatsapp",
blurb: "works with your own number; recommend a separate phone + eSIM.",
systemImage: "message"
},
discord: {
id: "discord",
label: "Discord",
selectionLabel: "Discord (Bot API)",
detailLabel: "Discord Bot",
docsPath: "/channels/discord",
docsLabel: "discord",
blurb: "very well supported right now.",
systemImage: "bubble.left.and.bubble.right"
},
googlechat: {
id: "googlechat",
label: "Google Chat",
selectionLabel: "Google Chat (Chat API)",
detailLabel: "Google Chat",
docsPath: "/channels/googlechat",
docsLabel: "googlechat",
blurb: "Google Workspace Chat app with HTTP webhook.",
systemImage: "message.badge"
},
slack: {
id: "slack",
label: "Slack",
selectionLabel: "Slack (Socket Mode)",
detailLabel: "Slack Bot",
docsPath: "/channels/slack",
docsLabel: "slack",
blurb: "supported (Socket Mode).",
systemImage: "number"
},
signal: {
id: "signal",
label: "Signal",
selectionLabel: "Signal (signal-cli)",
detailLabel: "Signal REST",
docsPath: "/channels/signal",
docsLabel: "signal",
blurb: "signal-cli linked device; more setup (David Reagans: \"Hop on Discord.\").",
systemImage: "antenna.radiowaves.left.and.right"
},
imessage: {
id: "imessage",
label: "iMessage",
selectionLabel: "iMessage (imsg)",
detailLabel: "iMessage",
docsPath: "/channels/imessage",
docsLabel: "imessage",
blurb: "this is still a work in progress.",
systemImage: "message.fill"
}
};
const CHAT_CHANNEL_ALIASES = {
imsg: "imessage",
"google-chat": "googlechat",
gchat: "googlechat"
};
const normalizeChannelKey = (raw) => {
return raw?.trim().toLowerCase() || void 0;
};
function getChatChannelMeta(id) {
return CHAT_CHANNEL_META[id];
}
function normalizeChatChannelId(raw) {
const normalized = normalizeChannelKey(raw);
if (!normalized) return null;
const resolved = CHAT_CHANNEL_ALIASES[normalized] ?? normalized;
return CHAT_CHANNEL_ORDER.includes(resolved) ? resolved : null;
}
function normalizeAnyChannelId(raw) {
const key = normalizeChannelKey(raw);
if (!key) return null;
return requireActivePluginRegistry().channels.find((entry) => {
const id = String(entry.plugin.id ?? "").trim().toLowerCase();
if (id && id === key) return true;
return (entry.plugin.meta.aliases ?? []).some((alias) => alias.trim().toLowerCase() === key);
})?.plugin.id ?? null;
}
//#endregion
//#region src/config/telegram-custom-commands.ts
const TELEGRAM_COMMAND_NAME_PATTERN = /^[a-z0-9_]{1,32}$/;
function normalizeTelegramCommandName(value) {
const trimmed = value.trim();
if (!trimmed) return "";
return (trimmed.startsWith("/") ? trimmed.slice(1) : trimmed).trim().toLowerCase();
}
function normalizeTelegramCommandDescription(value) {
return value.trim();
}
function resolveTelegramCustomCommands(params) {
const entries = Array.isArray(params.commands) ? params.commands : [];
const reserved = params.reservedCommands ?? /* @__PURE__ */ new Set();
const checkReserved = params.checkReserved !== false;
const checkDuplicates = params.checkDuplicates !== false;
const seen = /* @__PURE__ */ new Set();
const resolved = [];
const issues = [];
for (let index = 0; index < entries.length; index += 1) {
const entry = entries[index];
const normalized = normalizeTelegramCommandName(String(entry?.command ?? ""));
if (!normalized) {
issues.push({
index,
field: "command",
message: "Telegram custom command is missing a command name."
});
continue;
}
if (!TELEGRAM_COMMAND_NAME_PATTERN.test(normalized)) {
issues.push({
index,
field: "command",
message: `Telegram custom command "/${normalized}" is invalid (use a-z, 0-9, underscore; max 32 chars).`
});
continue;
}
if (checkReserved && reserved.has(normalized)) {
issues.push({
index,
field: "command",
message: `Telegram custom command "/${normalized}" conflicts with a native command.`
});
continue;
}
if (checkDuplicates && seen.has(normalized)) {
issues.push({
index,
field: "command",
message: `Telegram custom command "/${normalized}" is duplicated.`
});
continue;
}
const description = normalizeTelegramCommandDescription(String(entry?.description ?? ""));
if (!description) {
issues.push({
index,
field: "description",
message: `Telegram custom command "/${normalized}" is missing a description.`
});
continue;
}
if (checkDuplicates) seen.add(normalized);
resolved.push({
command: normalized,
description
});
}
return {
commands: resolved,
issues
};
}
//#endregion
//#region src/cli/parse-duration.ts
function parseDurationMs(raw, opts) {
const trimmed = String(raw ?? "").trim().toLowerCase();
if (!trimmed) throw new Error("invalid duration (empty)");
const m = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)?$/.exec(trimmed);
if (!m) throw new Error(`invalid duration: ${raw}`);
const value = Number(m[1]);
if (!Number.isFinite(value) || value < 0) throw new Error(`invalid duration: ${raw}`);
const unit = m[2] ?? opts?.defaultUnit ?? "ms";
const multiplier = unit === "ms" ? 1 : unit === "s" ? 1e3 : unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5;
const ms = Math.round(value * multiplier);
if (!Number.isFinite(ms)) throw new Error(`invalid duration: ${raw}`);
return ms;
}
//#endregion
//#region src/infra/exec-safety.ts
const SHELL_METACHARS = /[;&|`$<>]/;
const CONTROL_CHARS = /[\r\n]/;
const QUOTE_CHARS = /["']/;
const BARE_NAME_PATTERN = /^[A-Za-z0-9._+-]+$/;
function isLikelyPath(value) {
if (value.startsWith(".") || value.startsWith("~")) return true;
if (value.includes("/") || value.includes("\\")) return true;
return /^[A-Za-z]:[\\/]/.test(value);
}
function isSafeExecutableValue(value) {
if (!value) return false;
const trimmed = value.trim();
if (!trimmed) return false;
if (trimmed.includes("\0")) return false;
if (CONTROL_CHARS.test(trimmed)) return false;
if (SHELL_METACHARS.test(trimmed)) return false;
if (QUOTE_CHARS.test(trimmed)) return false;
if (isLikelyPath(trimmed)) return true;
if (trimmed.startsWith("-")) return false;
return BARE_NAME_PATTERN.test(trimmed);
}
//#endregion
//#region src/config/zod-schema.core.ts
const ModelApiSchema = z.union([
z.literal("openai-completions"),
z.literal("openai-responses"),
z.literal("anthropic-messages"),
z.literal("google-generative-ai"),
z.literal("github-copilot"),
z.literal("bedrock-converse-stream")
]);
const ModelCompatSchema = z.object({
supportsStore: z.boolean().optional(),
supportsDeveloperRole: z.boolean().optional(),
supportsReasoningEffort: z.boolean().optional(),
maxTokensField: z.union([z.literal("max_completion_tokens"), z.literal("max_tokens")]).optional()
}).strict().optional();
const ModelDefinitionSchema = z.object({
id: z.string().min(1),
name: z.string().min(1),
api: ModelApiSchema.optional(),
reasoning: z.boolean().optional(),
input: z.array(z.union([z.literal("text"), z.literal("image")])).optional(),
cost: z.object({
input: z.number().optional(),
output: z.number().optional(),
cacheRead: z.number().optional(),
cacheWrite: z.number().optional()
}).strict().optional(),
contextWindow: z.number().positive().optional(),
maxTokens: z.number().positive().optional(),
headers: z.record(z.string(), z.string()).optional(),
compat: ModelCompatSchema
}).strict();
const ModelProviderSchema = z.object({
baseUrl: z.string().min(1),
apiKey: z.string().optional(),
auth: z.union([
z.literal("api-key"),
z.literal("aws-sdk"),
z.literal("oauth"),
z.literal("token")
]).optional(),
api: ModelApiSchema.optional(),
headers: z.record(z.string(), z.string()).optional(),
authHeader: z.boolean().optional(),
models: z.array(ModelDefinitionSchema)
}).strict();
const BedrockDiscoverySchema = z.object({
enabled: z.boolean().optional(),
region: z.string().optional(),
providerFilter: z.array(z.string()).optional(),
refreshInterval: z.number().int().nonnegative().optional(),
defaultContextWindow: z.number().int().positive().optional(),
defaultMaxTokens: z.number().int().positive().optional()
}).strict().optional();
const ModelsConfigSchema = z.object({
mode: z.union([z.literal("merge"), z.literal("replace")]).optional(),
providers: z.record(z.string(), ModelProviderSchema).optional(),
bedrockDiscovery: BedrockDiscoverySchema
}).strict().optional();
const GroupChatSchema = z.object({
mentionPatterns: z.array(z.string()).optional(),
historyLimit: z.number().int().positive().optional()
}).strict().optional();
const DmConfigSchema = z.object({ historyLimit: z.number().int().min(0).optional() }).strict();
const IdentitySchema = z.object({
name: z.string().optional(),
theme: z.string().optional(),
emoji: z.string().optional(),
avatar: z.string().optional()
}).strict().optional();
const QueueModeSchema = z.union([
z.literal("steer"),
z.literal("followup"),
z.literal("collect"),
z.literal("steer-backlog"),
z.literal("steer+backlog"),
z.literal("queue"),
z.literal("interrupt")
]);
const QueueDropSchema = z.union([
z.literal("old"),
z.literal("new"),
z.literal("summarize")
]);
const ReplyToModeSchema = z.union([
z.literal("off"),
z.literal("first"),
z.literal("all")
]);
const GroupPolicySchema = z.enum([
"open",
"disabled",
"allowlist"
]);
const DmPolicySchema = z.enum([
"pairing",
"allowlist",
"open",
"disabled"
]);
const BlockStreamingCoalesceSchema = z.object({
minChars: z.number().int().positive().optional(),
maxChars: z.number().int().positive().optional(),
idleMs: z.number().int().nonnegative().optional()
}).strict();
const BlockStreamingChunkSchema = z.object({
minChars: z.number().int().positive().optional(),
maxChars: z.number().int().positive().optional(),
breakPreference: z.union([
z.literal("paragraph"),
z.literal("newline"),
z.literal("sentence")
]).optional()
}).strict();
const MarkdownTableModeSchema = z.enum([
"off",
"bullets",
"code"
]);
const MarkdownConfigSchema = z.object({ tables: MarkdownTableModeSchema.optional() }).strict().optional();
const TtsProviderSchema = z.enum([
"elevenlabs",
"openai",
"edge"
]);
const TtsModeSchema = z.enum(["final", "all"]);
const TtsAutoSchema = z.enum([
"off",
"always",
"inbound",
"tagged"
]);
const TtsConfigSchema = z.object({
auto: TtsAutoSchema.optional(),
enabled: z.boolean().optional(),
mode: TtsModeSchema.optional(),
provider: TtsProviderSchema.optional(),
summaryModel: z.string().optional(),
modelOverrides: z.object({
enabled: z.boolean().optional(),
allowText: z.boolean().optional(),
allowProvider: z.boolean().optional(),
allowVoice: z.boolean().optional(),
allowModelId: z.boolean().optional(),
allowVoiceSettings: z.boolean().optional(),
allowNormalization: z.boolean().optional(),
allowSeed: z.boolean().optional()
}).strict().optional(),
elevenlabs: z.object({
apiKey: z.string().optional(),
baseUrl: z.string().optional(),
voiceId: z.string().optional(),
modelId: z.string().optional(),
seed: z.number().int().min(0).max(4294967295).optional(),
applyTextNormalization: z.enum([
"auto",
"on",
"off"
]).optional(),
languageCode: z.string().optional(),
voiceSettings: z.object({
stability: z.number().min(0).max(1).optional(),
similarityBoost: z.number().min(0).max(1).optional(),
style: z.number().min(0).max(1).optional(),
useSpeakerBoost: z.boolean().optional(),
speed: z.number().min(.5).max(2).optional()
}).strict().optional()
}).strict().optional(),
openai: z.object({
apiKey: z.string().optional(),
model: z.string().optional(),
voice: z.string().optional()
}).strict().optional(),
edge: z.object({
enabled: z.boolean().optional(),
voice: z.string().optional(),
lang: z.string().optional(),
outputFormat: z.string().optional(),
pitch: z.string().optional(),
rate: z.string().optional(),
volume: z.string().optional(),
saveSubtitles: z.boolean().optional(),
proxy: z.string().optional(),
timeoutMs: z.number().int().min(1e3).max(12e4).optional()
}).strict().optional(),
prefsPath: z.string().optional(),
maxTextLength: z.number().int().min(1).optional(),
timeoutMs: z.number().int().min(1e3).max(12e4).optional()
}).strict().optional();
const HumanDelaySchema = z.object({
mode: z.union([
z.literal("off"),
z.literal("natural"),
z.literal("custom")
]).optional(),
minMs: z.number().int().nonnegative().optional(),
maxMs: z.number().int().nonnegative().optional()
}).strict();
const CliBackendSchema = z.object({
command: z.string(),
args: z.array(z.string()).optional(),
output: z.union([
z.literal("json"),
z.literal("text"),
z.literal("jsonl")
]).optional(),
resumeOutput: z.union([
z.literal("json"),
z.literal("text"),
z.literal("jsonl")
]).optional(),
input: z.union([z.literal("arg"), z.literal("stdin")]).optional(),
maxPromptArgChars: z.number().int().positive().optional(),
env: z.record(z.string(), z.string()).optional(),
clearEnv: z.array(z.string()).optional(),
modelArg: z.string().optional(),
modelAliases: z.record(z.string(), z.string()).optional(),
sessionArg: z.string().optional(),
sessionArgs: z.array(z.string()).optional(),
resumeArgs: z.array(z.string()).optional(),
sessionMode: z.union([
z.literal("always"),
z.literal("existing"),
z.literal("none")
]).optional(),
sessionIdFields: z.array(z.string()).optional(),
systemPromptArg: z.string().optional(),
systemPromptMode: z.union([z.literal("append"), z.literal("replace")]).optional(),
systemPromptWhen: z.union([
z.literal("first"),
z.literal("always"),
z.literal("never")
]).optional(),
imageArg: z.string().optional(),
imageMode: z.union([z.literal("repeat"), z.literal("list")]).optional(),
serialize: z.boolean().optional()
}).strict();
const normalizeAllowFrom = (values) => (values ?? []).map((v) => String(v).trim()).filter(Boolean);
const requireOpenAllowFrom = (params) => {
if (params.policy !== "open") return;
if (normalizeAllowFrom(params.allowFrom).includes("*")) return;
params.ctx.addIssue({
code: z.ZodIssueCode.custom,
path: params.path,
message: params.message
});
};
const MSTeamsReplyStyleSchema = z.enum(["thread", "top-level"]);
const RetryConfigSchema = z.object({
attempts: z.number().int().min(1).optional(),
minDelayMs: z.number().int().min(0).optional(),
maxDelayMs: z.number().int().min(0).optional(),
jitter: z.number().min(0).max(1).optional()
}).strict().optional();
const QueueModeBySurfaceSchema = z.object({
whatsapp: QueueModeSchema.optional(),
telegram: QueueModeSchema.optional(),
discord: QueueModeSchema.optional(),
slack: QueueModeSchema.optional(),
mattermost: QueueModeSchema.optional(),
signal: QueueModeSchema.optional(),
imessage: QueueModeSchema.optional(),
msteams: QueueModeSchema.optional(),
webchat: QueueModeSchema.optional()
}).strict().optional();
const DebounceMsBySurfaceSchema = z.record(z.string(), z.number().int().nonnegative()).optional();
const QueueSchema = z.object({
mode: QueueModeSchema.optional(),
byChannel: QueueModeBySurfaceSchema,
debounceMs: z.number().int().nonnegative().optional(),
debounceMsByChannel: DebounceMsBySurfaceSchema,
cap: z.number().int().positive().optional(),
drop: QueueDropSchema.optional()
}).strict().optional();
const InboundDebounceSchema = z.object({
debounceMs: z.number().int().nonnegative().optional(),
byChannel: DebounceMsBySurfaceSchema
}).strict().optional();
const TranscribeAudioSchema = z.object({
command: z.array(z.string()).superRefine((value, ctx) => {
const executable = value[0];
if (!isSafeExecutableValue(executable)) ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [0],
message: "expected safe executable name or path"
});
}),
timeoutSeconds: z.number().int().positive().optional()
}).strict().optional();
const HexColorSchema = z.string().regex(/^#?[0-9a-fA-F]{6}$/, "expected hex color (RRGGBB)");
const ExecutableTokenSchema = z.string().refine(isSafeExecutableValue, "expected safe executable name or path");
const MediaUnderstandingScopeSchema = z.object({
default: z.union([z.literal("allow"), z.literal("deny")]).optional(),
rules: z.array(z.object({
action: z.union([z.literal("allow"), z.literal("deny")]),
match: z.object({
channel: z.string().optional(),
chatType: z.union([
z.literal("direct"),
z.literal("group"),
z.literal("channel")
]).optional(),
keyPrefix: z.string().optional()
}).strict().optional()
}).strict()).optional()
}).strict().optional();
const MediaUnderstandingCapabilitiesSchema = z.array(z.union([
z.literal("image"),
z.literal("audio"),
z.literal("video")
])).optional();
const MediaUnderstandingAttachmentsSchema = z.object({
mode: z.union([z.literal("first"), z.literal("all")]).optional(),
maxAttachments: z.number().int().positive().optional(),
prefer: z.union([
z.literal("first"),
z.literal("last"),
z.literal("path"),
z.literal("url")
]).optional()
}).strict().optional();
const DeepgramAudioSchema = z.object({
detectLanguage: z.boolean().optional(),
punctuate: z.boolean().optional(),
smartFormat: z.boolean().optional()
}).strict().optional();
const ProviderOptionValueSchema = z.union([
z.string(),
z.number(),
z.boolean()
]);
const ProviderOptionsSchema = z.record(z.string(), z.record(z.string(), ProviderOptionValueSchema)).optional();
const MediaUnderstandingModelSchema = z.object({
provider: z.string().optional(),
model: z.string().optional(),
capabilities: MediaUnderstandingCapabilitiesSchema,
type: z.union([z.literal("provider"), z.literal("cli")]).optional(),
command: z.string().optional(),
args: z.array(z.string()).optional(),
prompt: z.string().optional(),
maxChars: z.number().int().positive().optional(),
maxBytes: z.number().int().positive().optional(),
timeoutSeconds: z.number().int().positive().optional(),
language: z.string().optional(),
providerOptions: ProviderOptionsSchema,
deepgram: DeepgramAudioSchema,
baseUrl: z.string().optional(),
headers: z.record(z.string(), z.string()).optional(),
profile: z.string().optional(),
preferredProfile: z.string().optional()
}).strict().optional();
const ToolsMediaUnderstandingSchema = z.object({
enabled: z.boolean().optional(),
scope: MediaUnderstandingScopeSchema,
maxBytes: z.number().int().positive().optional(),
maxChars: z.number().int().positive().optional(),
prompt: z.string().optional(),
timeoutSeconds: z.number().int().positive().optional(),
language: z.string().optional(),
providerOptions: ProviderOptionsSchema,
deepgram: DeepgramAudioSchema,
baseUrl: z.string().optional(),
headers: z.record(z.string(), z.string()).optional(),
attachments: MediaUnderstandingAttachmentsSchema,
models: z.array(MediaUnderstandingModelSchema).optional()
}).strict().optional();
const ToolsMediaSchema = z.object({
models: z.array(MediaUnderstandingModelSchema).optional(),
concurrency: z.number().int().positive().optional(),
image: ToolsMediaUnderstandingSchema.optional(),
audio: ToolsMediaUnderstandingSchema.optional(),
video: ToolsMediaUnderstandingSchema.optional()
}).strict().optional();
const LinkModelSchema = z.object({
type: z.literal("cli").optional(),
command: z.string().min(1),
args: z.array(z.string()).optional(),
timeoutSeconds: z.number().int().positive().optional()
}).strict();
const ToolsLinksSchema = z.object({
enabled: z.boolean().optional(),
scope: MediaUnderstandingScopeSchema,
maxLinks: z.number().int().positive().optional(),
timeoutSeconds: z.number().int().positive().optional(),
models: z.array(LinkModelSchema).optional()
}).strict().optional();
const NativeCommandsSettingSchema = z.union([z.boolean(), z.literal("auto")]);
const ProviderCommandsSchema = z.object({
native: NativeCommandsSettingSchema.optional(),
nativeSkills: NativeCommandsSettingSchema.optional()
}).strict().optional();
//#endregion
//#region src/config/zod-schema.agent-runtime.ts
const HeartbeatSchema = z.object({
every: z.string().optional(),
activeHours: z.object({
start: z.string().optional(),
end: z.string().optional(),
timezone: z.string().optional()
}).strict().optional(),
model: z.string().optional(),
session: z.string().optional(),
includeReasoning: z.boolean().optional(),
target: z.string().optional(),
to: z.string().optional(),
accountId: z.string().optional(),
prompt: z.string().optional(),
ackMaxChars: z.number().int().nonnegative().optional()
}).strict().superRefine((val, ctx) => {
if (!val.every) return;
try {
parseDurationMs(val.every, { defaultUnit: "m" });
} catch {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["every"],
message: "invalid duration (use ms, s, m, h)"
});
}
const active = val.activeHours;
if (!active) return;
const timePattern = /^([01]\d|2[0-3]|24):([0-5]\d)$/;
const validateTime = (raw, opts, path) => {
if (!raw) return;
if (!timePattern.test(raw)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["activeHours", path],
message: "invalid time (use \"HH:MM\" 24h format)"
});
return;
}
const [hourStr, minuteStr] = raw.split(":");
const hour = Number(hourStr);
if (hour === 24 && Number(minuteStr) !== 0) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["activeHours", path],
message: "invalid time (24:00 is the only allowed 24:xx value)"
});
return;
}
if (hour === 24 && !opts.allow24) ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["activeHours", path],
message: "invalid time (start cannot be 24:00)"
});
};
validateTime(active.start, { allow24: false }, "start");
validateTime(active.end, { allow24: true }, "end");
}).optional();
const SandboxDockerSchema = z.object({
image: z.string().optional(),
containerPrefix: z.string().optional(),
workdir: z.string().optional(),
readOnlyRoot: z.boolean().optional(),
tmpfs: z.array(z.string()).optional(),
network: z.string().optional(),
user: z.string().optional(),
capDrop: z.array(z.string()).optional(),
env: z.record(z.string(), z.string()).optional(),
setupCommand: z.string().optional(),
pidsLimit: z.number().int().positive().optional(),
memory: z.union([z.string(), z.number()]).optional(),
memorySwap: z.union([z.string(), z.number()]).optional(),
cpus: z.number().positive().optional(),
ulimits: z.record(z.string(), z.union([
z.string(),
z.number(),
z.object({
soft: z.number().int().nonnegative().optional(),
hard: z.number().int().nonnegative().optional()
}).strict()
])).optional(),
seccompProfile: z.string().optional(),
apparmorProfile: z.string().optional(),
dns: z.array(z.string()).optional(),
extraHosts: z.array(z.string()).optional(),
binds: z.array(z.string()).optional()
}).strict().optional();
const SandboxBrowserSchema = z.object({
enabled: z.boolean().optional(),
image: z.string().optional(),
containerPrefix: z.string().optional(),
cdpPort: z.number().int().positive().optional(),
vncPort: z.number().int().positive().optional(),
noVncPort: z.number().int().positive().optional(),
headless: z.boolean().optional(),
enableNoVnc: z.boolean().optional(),
allowHostControl: z.boolean().optional(),
autoStart: z.boolean().optional(),
autoStartTimeoutMs: z.number().int().positive().optional()
}).strict().optional();
const SandboxPruneSchema = z.object({
idleHours: z.number().int().nonnegative().optional(),
maxAgeDays: z.number().int().nonnegative().optional()
}).strict().optional();
const ToolPolicyBaseSchema = z.object({
allow: z.array(z.string()).optional(),
alsoAllow: z.array(z.string()).optional(),
deny: z.array(z.string()).optional()
}).strict();
const ToolPolicySchema = ToolPolicyBaseSchema.superRefine((value, ctx) => {
if (value.allow && value.allow.length > 0 && value.alsoAllow && value.alsoAllow.length > 0) ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "tools policy cannot set both allow and alsoAllow in the same scope (merge alsoAllow into allow, or remove allow and use profile + alsoAllow)"
});
}).optional();
const ToolsWebSearchSchema = z.object({
enabled: z.boolean().optional(),
provider: z.union([z.literal("brave"), z.literal("perplexity")]).optional(),
apiKey: z.string().optional(),
maxResults: z.number().int().positive().optional(),
timeoutSeconds: z.number().int().positive().optional(),
cacheTtlMinutes: z.number().nonnegative().optional(),
perplexity: z.object({
apiKey: z.string().optional(),
baseUrl: z.string().optional(),
model: z.string().optional()
}).strict().optional()
}).strict().optional();
const ToolsWebFetchSchema = z.object({
enabled: z.boolean().optional(),
maxChars: z.number().int().positive().optional(),
maxCharsCap: z.number().int().positive().optional(),
timeoutSeconds: z.number().int().positive().optional(),
cacheTtlMinutes: z.number().nonnegative().optional(),
maxRedirects: z.number().int().nonnegative().optional(),
userAgent: z.string().optional()
}).strict().optional();
const ToolsWebSchema = z.object({
search: ToolsWebSearchSchema,
fetch: ToolsWebFetchSchema
}).strict().optional();
const ToolProfileSchema = z.union([
z.literal("minimal"),
z.literal("coding"),
z.literal("messaging"),
z.literal("full")
]).optional();
const ToolPolicyWithProfileSchema = z.object({
allow: z.array(z.string()).optional(),
alsoAllow: z.array(z.string()).optional(),
deny: z.array(z.string()).optional(),
profile: ToolProfileSchema
}).strict().superRefine((value, ctx) => {
if (value.allow && value.allow.length > 0 && value.alsoAllow && value.alsoAllow.length > 0) ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "tools.byProvider policy cannot set both allow and alsoAllow in the same scope (merge alsoAllow into allow, or remove allow and use profile + alsoAllow)"
});
});
const ElevatedAllowFromSchema = z.record(z.string(), z.array(z.union([z.string(), z.number()]))).optional();
const AgentSandboxSchema = z.object({
mode: z.union([
z.literal("off"),
z.literal("non-main"),
z.literal("all")
]).optional(),
workspaceAccess: z.union([
z.literal("none"),
z.literal("ro"),
z.literal("rw")
]).optional(),
sessionToolsVisibility: z.union([z.literal("spawned"), z.literal("all")]).optional(),
scope: z.union([
z.literal("session"),
z.literal("agent"),
z.literal("shared")
]).optional(),
perSession: z.boolean().optional(),
workspaceRoot: z.string().optional(),
docker: SandboxDockerSchema,
browser: SandboxBrowserSchema,
prune: SandboxPruneSchema
}).strict().optional();
const AgentToolsSchema = z.object({
profile: ToolProfileSchema,
allow: z.array(z.string()).optional(),
alsoAllow: z.array(z.string()).optional(),
deny: z.array(z.string()).optional(),
byProvider: z.record(z.string(), ToolPolicyWithProfileSchema).optional(),
elevated: z.object({
enabled: z.boolean().optional(),
allowFrom: ElevatedAllowFromSchema
}).strict().optional(),
exec: z.object({
host: z.enum([
"sandbox",
"gateway",
"node"
]).optional(),
security: z.enum([
"deny",
"allowlist",
"full"
]).optional(),
ask: z.enum([
"off",
"on-miss",
"always"
]).optional(),
node: z.string().optional(),
pathPrepend: z.array(z.string()).optional(),
safeBins: z.array(z.string()).optional(),
backgroundMs: z.number().int().positive().optional(),
timeoutSec: z.number().int().positive().optional(),
approvalRunningNoticeMs: z.number().int().nonnegative().optional(),
cleanupMs: z.number().int().positive().optional(),
notifyOnExit: z.boolean().optional(),
applyPatch: z.object({
enabled: z.boolean().optional(),
allowModels: z.array(z.string()).optional()
}).strict().optional()
}).strict().optional(),
sandbox: z.object({ tools: ToolPolicySchema }).strict().optional()
}).strict().superRefine((value, ctx) => {
if (value.allow && value.allow.length > 0 && value.alsoAllow && value.alsoAllow.length > 0) ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "agent tools cannot set both allow and alsoAllow in the same scope (merge alsoAllow into allow, or remove allow and use profile + alsoAllow)"
});
}).optional();
const MemorySearchSchema$1 = z.object({
enabled: z.boolean().optional(),
sources: z.array(z.union([z.literal("memory"), z.literal("sessions")])).optional(),
extraPaths: z.array(z.string()).optional(),
experimental: z.object({ sessionMemory: z.boolean().optional() }).strict().optional(),
provider: z.union([
z.literal("openai"),
z.literal("local"),
z.literal("gemini")
]).optional(),
remote: z.object({
baseUrl: z.string().optional(),
apiKey: z.string().optional(),
headers: z.record(z.string(), z.string()).optional(),
batch: z.object({
enabled: z.boolean().optional(),
wait: z.boolean().optional(),
concurrency: z.number().int().positive().optional(),
pollIntervalMs: z.number().int().nonnegative().optional(),
timeoutMinutes: z.number().int().positive().optional()
}).strict().optional()
}).strict().optional(),
fallback: z.union([
z.literal("openai"),
z.literal("gemini"),
z.literal("local"),
z.literal("none")
]).optional(),
model: z.string().optional(),
local: z.object({
modelPath: z.string().optional(),
modelCacheDir: z.string().optional()
}).strict().optional(),
store: z.object({
driver: z.literal("sqlite").optional(),
path: z.string().optional(),
vector: z.object({
enabled: z.boolean().optional(),
extensionPath: z.string().optional()
}).strict().optional()
}).strict().optional(),
chunking: z.object({
tokens: z.number().int().positive().optional(),
overlap: z.number().int().nonnegative().optional()
}).strict().optional(),
sync: z.object({
onSessionStart: z.boolean().optional(),
onSearch: z.boolean().optional(),
watch: z.boolean().optional(),
watchDebounceMs: z.number().int().nonnegative().optional(),
intervalMinutes: z.number().int().nonnegative().optional(),
sessions: z.object({
deltaBytes: z.number().int().nonnegative().optional(),
deltaMessages: z.number().int().nonnegative().optional()
}).strict().optional()
}).strict().optional(),
query: z.object({
maxResults: z.number().int().positive().optional(),
minScore: z.number().min(0).max(1).optional(),
hybrid: z.object({
enabled: z.boolean().optional(),
vectorWeight: z.number().min(0).max(1).optional(),
textWeight: z.number().min(0).max(1).optional(),
candidateMultiplier: z.number().int().positive().optional()
}).strict().optional()
}).strict().optional(),
cache: z.object({
enabled: z.boolean().optional(),
maxEntries: z.number().int().positive().optional()
}).strict().optional()
}).strict().optional();
const AgentModelSchema = z.union([z.string(), z.object({
primary: z.string().optional(),
fallbacks: z.array(z.string()).optional()
}).strict()]);
const AgentEntrySchema = z.object({
id: z.string(),
default: z.boolean().optional(),
name: z.string().optional(),
workspace: z.string().optional(),
agentDir: z.string().optional(),
model: AgentModelSchema.optional(),
skills: z.array(z.string()).optional(),
memorySearch: MemorySearchSchema$1,
humanDelay: HumanDelaySchema.optional(),
heartbeat: HeartbeatSchema,
identity: IdentitySchema,
groupChat: GroupChatSchema,
subagents: z.object({
allowAgents: z.array(z.string()).optional(),
model: z.union([z.string(), z.object({
primary: z.string().optional(),
fallbacks: z.array(z.string()).optional()
}).strict()]).optional(),
thinking: z.string().optional()
}).strict().optional(),
sandbox: AgentSandboxSchema,
tools: AgentToolsSchema
}).strict();
const ToolsSchema = z.object({
profile: ToolProfileSchema,
allow: z.array(z.string()).optional(),
alsoAllow: z.array(z.string()).optional(),
deny: z.array(z.string()).optional(),
byProvider: z.record(z.string(), ToolPolicyWithProfileSchema).optional(),
web: ToolsWebSchema,
media: ToolsMediaSchema,
links: ToolsLinksSchema,
message: z.object({
allowCrossContextSend: z.boolean().optional(),
crossContext: z.object({
allowWithinProvider: z.boolean().optional(),
allowAcrossProviders: z.boolean().optional(),
marker: z.object({
enabled: z.boolean().optional(),
prefix: z.string().optional(),
suffix: z.string().optional()
}).strict().optional()
}).strict().optional(),
broadcast: z.object({ enabled: z.boolean().optional() }).strict().optional()
}).strict().optional(),
agentToAgent: z.object({
enabled: z.boolean().optional(),
allow: z.array(z.string()).optional()
}).strict().optional(),
elevated: z.object({
enabled: z.boolean().optional(),
allowFrom: ElevatedAllowFromSchema
}).strict().optional(),
exec: z.object({
host: z.enum([
"sandbox",
"gateway",
"node"
]).optional(),
security: z.enum([
"deny",
"allowlist",
"full"
]).optional(),
ask: z.enum([
"off",
"on-miss",
"always"
]).optional(),
node: z.string().optional(),
pathPrepend: z.array(z.string()).optional(),
safeBins: z.array(z.string()).optional(),
backgroundMs: z.number().int().positive().optional(),
timeoutSec: z.number().int().positive().optional(),
cleanupMs: z.number().int().positive().optional(),
notifyOnExit: z.boolean().optional(),
applyPatch: z.object({
enabled: z.boolean().optional(),
allowModels: z.array(z.string()).optional()
}).strict().optional()
}).strict().optional(),
subagents: z.object({ tools: ToolPolicySchema }).strict().optional(),
sandbox: z.object({ tools: ToolPolicySchema }).strict().optional()
}).strict().superRefine((value, ctx) => {
if (value.allow && value.allow.length > 0 && value.alsoAllow && value.alsoAllow.length > 0) ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "tools cannot set both allow and alsoAllow in the same scope (merge alsoAllow into allow, or remove allow and use profile + alsoAllow)"
});
}).optional();
//#endregion
//#region src/config/zod-schema.channels.ts
const ChannelHeartbeatVisibilitySchema = z.object({
showOk: z.boolean().optional(),
showAlerts: z.boolean().optional(),
useIndicator: z.boolean().optional()
}).strict().optional();
//#endregion
//#region src/config/zod-schema.providers-core.ts
const ToolPolicyBySenderSchema$1 = z.record(z.string(), ToolPolicySchema).optional();
const TelegramInlineButtonsScopeSchema = z.enum([
"off",
"dm",
"group",
"all",
"allowlist"
]);
const TelegramCapabilitiesSchema = z.union([z.array(z.string()), z.object({ inlineButtons: TelegramInlineButtonsScopeSchema.optional() }).strict()]);
const TelegramTopicSchema = z.object({
requireMention: z.boolean().optional(),
groupPolicy: GroupPolicySchema.optional(),
skills: z.array(z.string()).optional(),
enabled: z.boolean().optional(),
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
systemPrompt: z.string().optional()
}).strict();
const TelegramGroupSchema = z.object({
requireMention: z.boolean().optional(),
groupPolicy: GroupPolicySchema.optional(),
tools: ToolPolicySchema,
toolsBySender: ToolPolicyBySenderSchema$1,
skills: z.array(z.string()).optional(),
enabled: z.boolean().optional(),
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
systemPrompt: z.string().optional(),
topics: z.record(z.string(), TelegramTopicSchema.optional()).optional()
}).strict();
const TelegramCustomCommandSchema = z.object({
command: z.string().transform(normalizeTelegramCommandName),
description: z.string().transform(normalizeTelegramCommandDescription)
}).strict();
const validateTelegramCustomCommands = (value, ctx) => {
if (!value.customCommands || value.customCommands.length === 0) return;
const { issues } = resolveTelegramCustomCommands({
commands: value.customCommands,
checkReserved: false,
checkDuplicates: false
});
for (const issue of issues) ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [
"customCommands",
issue.index,
issue.field
],
message: issue.message
});
};
const TelegramAccountSchemaBase = z.object({
name: z.string().optional(),
capabilities: TelegramCapabilitiesSchema.optional(),
markdown: MarkdownConfigSchema,
enabled: z.boolean().optional(),
commands: ProviderCommandsSchema,
customCommands: z.array(TelegramCustomCommandSchema).optional(),
configWrites: z.boolean().optional(),
dmPolicy: DmPolicySchema.optional().default("pairing"),
botToken: z.string().optional(),
tokenFile: z.string().optional(),
replyToMode: ReplyToModeSchema.optional(),
groups: z.record(z.string(), TelegramGroupSchema.optional()).optional(),
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
historyLimit: z.number().int().min(0).optional(),
dmHistoryLimit: z.number().int().min(0).optional(),
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
textChunkLimit: z.number().int().positive().optional(),
chunkMode: z.enum(["length", "newline"]).optional(),
blockStreaming: z.boolean().optional(),
draftChunk: BlockStreamingChunkSchema.optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
streamMode: z.enum([
"off",
"partial",
"block"
]).optional().default("partial"),
mediaMaxMb: z.number().positive().optional(),
timeoutSeconds: z.number().int().positive().optional(),
retry: RetryConfigSchema,
network: z.object({ autoSelectFamily: z.boolean().optional() }).strict().optional(),
proxy: z.string().optional(),
webhookUrl: z.string().optional(),
webhookSecret: z.string().optional(),
webhookPath: z.string().optional(),
actions: z.object({
reactions: z.boolean().optional(),
sendMessage: z.boolean().optional(),
deleteMessage: z.boolean().optional(),
sticker: z.boolean().optional()
}).strict().optional(),
reactionNotifications: z.enum([
"off",
"own",
"all"
]).optional(),
reactionLevel: z.enum([
"off",
"ack",
"minimal",
"extensive"
]).optional(),
heartbeat: ChannelHeartbeatVisibilitySchema,
linkPreview: z.boolean().optional(),
responsePrefix: z.string().optional()
}).strict();
const TelegramAccountSchema = TelegramAccountSchemaBase.superRefine((value, ctx) => {
requireOpenAllowFrom({
policy: value.dmPolicy,
allowFrom: value.allowFrom,
ctx,
path: ["allowFrom"],
message: "channels.telegram.dmPolicy=\"open\" requires channels.telegram.allowFrom to include \"*\""
});
validateTelegramCustomCommands(value, ctx);
});
const TelegramConfigSchema = TelegramAccountSchemaBase.extend({ accounts: z.record(z.string(), TelegramAccountSchema.optional()).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 \"*\""
});
validateTelegramCustomCommands(value, ctx);
const baseWebhookUrl = typeof value.webhookUrl === "string" ? value.webhookUrl.trim() : "";
const baseWebhookSecret = typeof value.webhookSecret === "string" ? value.webhookSecret.trim() : "";
if (baseWebhookUrl && !baseWebhookSecret) ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "channels.telegram.webhookUrl requires channels.telegram.webhookSecret",
path: ["webhookSecret"]
});
if (!value.accounts) return;
for (const [accountId, account] of Object.entries(value.accounts)) {
if (!account) continue;
if (account.enabled === false) continue;
if (!(typeof account.webhookUrl === "string" ? account.webhookUrl.trim() : "")) continue;
if (!(typeof account.webhookSecret === "string" ? account.webhookSecret.trim() : "") && !baseWebhookSecret) ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "channels.telegram.accounts.*.webhookUrl requires channels.telegram.webhookSecret or channels.telegram.accounts.*.webhookSecret",
path: [
"accounts",
accountId,
"webhookSecret"
]
});
}
});
const DiscordDmSchema = z.object({
enabled: z.boolean().optional(),
policy: DmPolicySchema.optional().default("pairing"),
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groupEnabled: z.boolean().optional(),
groupChannels: z.array(z.union([z.string(), z.number()])).optional()
}).strict().superRefine((value, ctx) => {
requireOpenAllowFrom({
policy: value.policy,
allowFrom: value.allowFrom,
ctx,
path: ["allowFrom"],
message: "channels.discord.dm.policy=\"open\" requires channels.discord.dm.allowFrom to include \"*\""
});
});
const DiscordGuildChannelSchema = z.object({
allow: z.boolean().optional(),
requireMention: z.boolean().optional(),
tools: ToolPolicySchema,
toolsBySender: ToolPolicyBySenderSchema$1,
skills: z.array(z.string()).optional(),
enabled: z.boolean().optional(),
users: z.array(z.union([z.string(), z.number()])).optional(),
systemPrompt: z.string().optional(),
includeThreadStarter: z.boolean().optional(),
autoThread: z.boolean().optional()
}).strict();
const DiscordGuildSchema = z.object({
slug: z.string().optional(),
requireMention: z.boolean().optional(),
tools: ToolPolicySchema,
toolsBySender: ToolPolicyBySenderSchema$1,
reactionNotifications: z.enum([
"off",
"own",
"all",
"allowlist"
]).optional(),
users: z.array(z.union([z.string(), z.number()])).optional(),
channels: z.record(z.string(), DiscordGuildChannelSchema.optional()).optional()
}).strict();
const DiscordAccountSchema = z.object({
name: z.string().optional(),
capabilities: z.array(z.string()).optional(),
markdown: MarkdownConfigSchema,
enabled: z.boolean().optional(),
commands: ProviderCommandsSchema,
configWrites: z.boolean().optional(),
token: z.string().optional(),
allowBots: z.boolean().optional(),
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
historyLimit: z.number().int().min(0).optional(),
dmHistoryLimit: z.number().int().min(0).optional(),
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
textChunkLimit: z.number().int().positive().optional(),
chunkMode: z.enum(["length", "newline"]).optional(),
blockStreaming: z.boolean().optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
maxLinesPerMessage: z.number().int().positive().optional(),
mediaMaxMb: z.number().positive().optional(),
retry: RetryConfigSchema,
actions: z.object({
reactions: z.boolean().optional(),
stickers: z.boolean().optional(),
emojiUploads: z.boolean().optional(),
stickerUploads: z.boolean().optional(),
polls: z.boolean().optional(),
permissions: z.boolean().optional(),
messages: z.boolean().optional(),
threads: z.boolean().optional(),
pins: z.boolean().optional(),
search: z.boolean().optional(),
memberInfo: z.bool