UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

1,371 lines (1,363 loc) 199 kB
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(),