@gguf/claw
Version:
WhatsApp gateway CLI (Baileys web) with Pi RPC agent
1,371 lines (1,363 loc) • 199 kB
JavaScript
import { G as CHANNEL_IDS, I as success, M as info, S as resolveConfigDir, V as getChildLogger, X as normalizeChatChannelId, b as jidToE164, u as defaultRuntime, w as resolveUserPath } from "./exec-CjyVHUuE.js";
import { A as normalizeAccountId, T as DEFAULT_AGENT_ID, c as resolveDefaultAgentId, j as normalizeAgentId, s as resolveAgentWorkspaceDir, w as DEFAULT_ACCOUNT_ID } from "./agent-scope-qNPdmst1.js";
import { a as resolveOAuthDir, n as resolveConfigPath, r as resolveDefaultConfigCandidates, s as resolveStateDir } from "./paths-BFxmmTT5.js";
import { H as resolveShellEnvFallbackTimeoutMs, L as DEFAULT_CONTEXT_TOKENS, U as shouldDeferShellEnvFallback, V as loadShellEnvFallback, W as shouldEnableShellEnvFallback, o as parseModelRef } from "./model-selection-Bo7pocNu.js";
import { createRequire } from "node:module";
import { z } from "zod";
import os from "node:os";
import path from "node:path";
import fs from "node:fs";
import json5 from "json5";
import fs$1 from "node:fs/promises";
import { fileURLToPath } from "node:url";
import crypto from "node:crypto";
import AjvPkg from "ajv";
//#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(),
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 = 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,
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(),
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(),
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()
}).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(),
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.boolean().optional(),
roleInfo: z.boolean().optional(),
roles: z.boolean().optional(),
channelInfo: z.boolean().optional(),
voiceStatus: z.boolean().optional(),
events: z.boolean().optional(),
moderation: z.boolean().optional(),
channels: z.boolean().optional(),
presence: z.boolean().optional()
}).strict().optional(),
replyToMode: ReplyToModeSchema.optional(),
dm: DiscordDmSchema.optional(),
guilds: z.record(z.string(), DiscordGuildSchema.optional()).optional(),
heartbeat: ChannelHeartbeatVisibilitySchema,
execApprovals: z.object({
enabled: z.boolean().optional(),
approvers: z.array(z.union([z.string(), z.number()])).optional(),
agentFilter: z.array(z.string()).optional(),
sessionFilter: z.array(z.string()).optional()
}).strict().optional(),
intents: z.object({
presence: z.boolean().optional(),
guildMembers: z.boolean().optional()
}).strict().optional(),
pluralkit: z.object({
enabled: z.boolean().optional(),
token: z.string().optional()
}).strict().optional()
}).strict();
const DiscordConfigSchema = DiscordAccountSchema.extend({ accounts: z.record(z.string(), DiscordAccountSchema.optional()).optional() });
const GoogleChatDmSchema = z.object({
enabled: z.boolean().optional(),
policy: DmPolicySchema.optional().default("pairing"),
allowFrom: 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.googlechat.dm.policy=\"open\" requires channels.googlechat.dm.allowFrom to include \"*\""
});
});
const GoogleChatGroupSchema = z.object({
enabled: z.boolean().optional(),
allow: z.boolean().optional(),
requireMention: z.boolean().optional(),
users: z.array(z.union([z.string(), z.number()])).optional(),
systemPrompt: z.string().optional()
}).strict();
const GoogleChatAccountSchema = z.object({
name: z.string().optional(),
capabilities: z.array(z.string()).optional(),
enabled: z.boolean().optional(),
configWrites: z.boolean().optional(),
allowBots: z.boolean().optional(),
requireMention: z.boolean().optional(),
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groups: z.record(z.string(), GoogleChatGroupSchema.optional()).optional(),
serviceAccount: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
serviceAccountFile: z.string().optional(),
audienceType: z.enum(["app-url", "project-number"]).optional(),
audience: z.string().optional(),
webhookPath: z.string().optional(),
webhookUrl: z.string().optional(),
botUser: z.string().optional(),
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(),
mediaMaxMb: z.number().positive().optional(),
replyToMode: ReplyToModeSchema.optional(),
actions: z.object({ reactions: z.boolean().optional() }).strict().optional(),
dm: GoogleChatDmSchema.optional(),
typingIndicator: z.enum([
"none",
"message",
"reaction"
]).optional()
}).strict();
const GoogleChatConfigSchema = GoogleChatAccountSchema.extend({
accounts: z.record(z.string(), GoogleChatAccountSchema.optional()).optional(),
defaultAccount: z.string().optional()
});
const SlackDmSchema = 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(),
replyToMode: ReplyToModeSchema.optional()
}).strict().superRefine((value, ctx) => {
requireOpenAllowFrom({
policy: value.policy,
allowFrom: value.allowFrom,
ctx,
path: ["allowFrom"],
message: "channels.slack.dm.policy=\"open\" requires channels.slack.dm.allowFrom to include \"*\""
});
});
const SlackChannelSchema = z.object({
enabled: z.boolean().optional(),
allow: z.boolean().optional(),
requireMention: z.boolean().optional(),
tools: ToolPolicySchema,
toolsBySender: ToolPolicyBySenderSchema$1,
allowBots: z.boolean().optional(),
users: z.array(z.union([z.string(), z.number()])).optional(),
skills: z.array(z.string()).optional(),
systemPrompt: z.string().optional()
}).strict();
const SlackThreadSchema = z.object({
historyScope: z.enum(["thread", "channel"]).optional(),
inheritParent: z.boolean().optional()
}).strict();
const SlackReplyToModeByChatTypeSchema = z.object({
direct: ReplyToModeSchema.optional(),
group: ReplyToModeSchema.optional(),
channel: ReplyToModeSchema.optional()
}).strict();
const SlackAccountSchema = z.object({
name: z.string().optional(),
mode: z.enum(["socket", "http"]).optional(),
signingSecret: z.string().optional(),
webhookPath: z.string().optional(),
capabilities: z.array(z.string()).optional(),
markdown: MarkdownConfigSchema,
enabled: z.boolean().optional(),
commands: ProviderCommandsSchema,
configWrites: z.boolean().optional(),
botToken: z.string().optional(),
appToken: z.string().optional(),
userToken: z.string().optional(),
userTokenReadOnly: z.boolean().optional().default(true),
allowBots: z.boolean().optional(),
requireMention: 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(),
mediaMaxMb: z.number().positive().optional(),
reactionNotifications: z.enum([
"off",
"own",
"all",
"allowlist"
]).optional(),
reactionAllowlist: z.array(z.union([z.string(), z.number()])).optional(),
replyToMode: ReplyToModeSchema.optional(),
replyToModeByChatType: SlackReplyToModeByChatTypeSchema.optional(),
thread: SlackThreadSchema.optional(),
actions: z.object({
reactions: z.boolean().optional(),
messages: z.boolean().optional(),
pins: z.boolean().optional(),
search: z.boolean().optional(),
permissions: z.boolean().optional(),
memberInfo: z.boolean().optional(),
channelInfo: z.boolean().optional(),
emojiList: z.boolean().optional()
}).strict().optional(),
slashCommand: z.object({
enabled: z.boolean().optional(),
name: z.string().optional(),
sessionPrefix: z.string().optional(),
ephemeral: z.boolean().optional()
}).strict().optional(),
dm: SlackDmSchema.optional(),
channels: z.record(z.string(), SlackChannelSchema.optional()).optional(),
heartbeat: ChannelHeartbeatVisibilitySchema
}).strict();
const SlackConfigSchema = SlackAccountSchema.extend({
mode: z.enum(["socket", "http"]).optional().default("socket"),
signingSecret: z.string().optional(),
webhookPath: z.string().optional().default("/slack/events"),
accounts: z.record(z.string(), SlackAccountSchema.optional()).optional()
}).superRefine((value, ctx) => {
const baseMode = value.mode ?? "socket";
if (baseMode === "http" && !value.signingSecret) ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "channels.slack.mode=\"http\" requires channels.slack.signingSecret",
path: ["signingSecret"]
});
if (!value.accounts) return;
for (const [accountId, account] of Object.entries(value.accounts)) {
if (!account) continue;
if (account.enabled === false) continue;
if ((account.mode ?? baseMode) !== "http") continue;
if (!(account.signingSecret ?? value.signingSecret)) ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "channels.slack.accounts.*.mode=\"http\" requires channels.slack.signingSecret or channels.slack.accounts.*.signingSecret",
path: [
"accounts",
accountId,
"signingSecret"
]
});
}
});
const SignalAccountSchemaBase = z.object({
name: z.string().optional(),
capabilities: z.array(z.string()).optional(),
markdown: MarkdownConfigSchema,
enabled: z.boolean().optional(),
configWrites: z.boolean().optional(),
account: z.string().optional(),
httpUrl: z.string().optional(),
httpHost: z.string().optional(),
httpPort: z.number().int().positive().optional(),
cliPath: ExecutableTokenSchema.optional(),
autoStart: z.boolean().optional(),
startupTimeoutMs: z.number().int().min(1e3).max(12e4).optional(),
receiveMode: z.union([z.literal("on-start"), z.literal("manual")]).optional(),
ignoreAttachments: z.boolean().optional(),
ignoreStories: z.boolean().optional(),
sendReadReceipts: z.boolean().optional(),
dmPolicy: DmPolicySchema.optional().default("pairing"),
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),