UNPKG

@gguf/claw

Version:

WhatsApp gateway CLI (Baileys web) with Pi RPC agent

1,027 lines (1,026 loc) 352 kB
import { ZodTypeAny, z } from "zod"; import fs from "node:fs"; import { Logger } from "tslog"; import json5 from "json5"; import "chalk"; import { ImageContent, OAuthCredentials } from "@mariozechner/pi-ai"; import AjvPkg from "ajv"; import { AuthStorage, ModelRegistry, Skill } from "@mariozechner/pi-coding-agent"; import * as _sinclair_typebox1712 from "@sinclair/typebox"; import { Static, TSchema } from "@sinclair/typebox"; import { IncomingMessage, ServerResponse } from "node:http"; import { lookup } from "node:dns"; import { lookup as lookup$1 } from "node:dns/promises"; import "undici"; import { ChannelType } from "discord-api-types/v10"; import { Button, ChannelType as ChannelType$1, Client, Command, MessageCreateListener, MessageReactionAddListener, MessageReactionRemoveListener, PresenceUpdateListener, RequestClient } from "@buape/carbon"; import { WebSocket as WebSocket$1 } from "ws"; import { WebClient } from "@slack/web-api"; import { Bot } from "grammy"; import { WebhookRequestBody, messagingApi } from "@line/bot-sdk"; import "@grammyjs/runner"; import { AnyMessageContent, makeWASocket } from "@whiskeysockets/baileys"; import { AgentMessage, AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core"; import { Command as Command$1 } from "commander"; //#region src/channels/plugins/message-action-names.d.ts declare const CHANNEL_MESSAGE_ACTION_NAMES: readonly ["send", "broadcast", "poll", "react", "reactions", "read", "edit", "unsend", "reply", "sendWithEffect", "renameGroup", "setGroupIcon", "addParticipant", "removeParticipant", "leaveGroup", "sendAttachment", "delete", "pin", "unpin", "list-pins", "permissions", "thread-create", "thread-list", "thread-reply", "search", "sticker", "sticker-search", "member-info", "role-info", "emoji-list", "emoji-upload", "sticker-upload", "role-add", "role-remove", "channel-info", "channel-list", "channel-create", "channel-edit", "channel-delete", "channel-move", "category-create", "category-edit", "category-delete", "voice-status", "event-list", "event-create", "timeout", "kick", "ban", "set-presence"]; type ChannelMessageActionName$2 = (typeof CHANNEL_MESSAGE_ACTION_NAMES)[number]; //#endregion //#region src/channels/plugins/bluebubbles-actions.d.ts declare const BLUEBUBBLES_ACTIONS: { readonly react: { readonly gate: "reactions"; }; readonly edit: { readonly gate: "edit"; readonly unsupportedOnMacOS26: true; }; readonly unsend: { readonly gate: "unsend"; }; readonly reply: { readonly gate: "reply"; }; readonly sendWithEffect: { readonly gate: "sendWithEffect"; }; readonly renameGroup: { readonly gate: "renameGroup"; readonly groupOnly: true; }; readonly setGroupIcon: { readonly gate: "setGroupIcon"; readonly groupOnly: true; }; readonly addParticipant: { readonly gate: "addParticipant"; readonly groupOnly: true; }; readonly removeParticipant: { readonly gate: "removeParticipant"; readonly groupOnly: true; }; readonly leaveGroup: { readonly gate: "leaveGroup"; readonly groupOnly: true; }; readonly sendAttachment: { readonly gate: "sendAttachment"; }; }; declare const BLUEBUBBLES_ACTION_NAMES: (keyof typeof BLUEBUBBLES_ACTIONS)[]; declare const BLUEBUBBLES_GROUP_ACTIONS: Set<"send" | "broadcast" | "poll" | "react" | "reactions" | "read" | "edit" | "unsend" | "reply" | "sendWithEffect" | "renameGroup" | "setGroupIcon" | "addParticipant" | "removeParticipant" | "leaveGroup" | "sendAttachment" | "delete" | "pin" | "unpin" | "list-pins" | "permissions" | "thread-create" | "thread-list" | "thread-reply" | "search" | "sticker" | "sticker-search" | "member-info" | "role-info" | "emoji-list" | "emoji-upload" | "sticker-upload" | "role-add" | "role-remove" | "channel-info" | "channel-list" | "channel-create" | "channel-edit" | "channel-delete" | "channel-move" | "category-create" | "category-edit" | "category-delete" | "voice-status" | "event-list" | "event-create" | "timeout" | "kick" | "ban" | "set-presence">; //#endregion //#region src/auto-reply/reply/typing.d.ts type TypingController = { onReplyStart: () => Promise<void>; startTypingLoop: () => Promise<void>; startTypingOnText: (text?: string) => Promise<void>; refreshTypingTtl: () => void; isActive: () => boolean; markRunComplete: () => void; markDispatchIdle: () => void; cleanup: () => void; }; //#endregion //#region src/auto-reply/types.d.ts type BlockReplyContext = { abortSignal?: AbortSignal; timeoutMs?: number; }; /** Context passed to onModelSelected callback with actual model used. */ type ModelSelectedContext = { provider: string; model: string; thinkLevel: string | undefined; }; type GetReplyOptions = { /** Override run id for agent events (defaults to random UUID). */runId?: string; /** Abort signal for the underlying agent run. */ abortSignal?: AbortSignal; /** Optional inbound images (used for webchat attachments). */ images?: ImageContent[]; /** Notifies when an agent run actually starts (useful for webchat command handling). */ onAgentRunStart?: (runId: string) => void; onReplyStart?: () => Promise<void> | void; onTypingController?: (typing: TypingController) => void; isHeartbeat?: boolean; onPartialReply?: (payload: ReplyPayload) => Promise<void> | void; onReasoningStream?: (payload: ReplyPayload) => Promise<void> | void; onBlockReply?: (payload: ReplyPayload, context?: BlockReplyContext) => Promise<void> | void; onToolResult?: (payload: ReplyPayload) => Promise<void> | void; /** Called when the actual model is selected (including after fallback). * Use this to get model/provider/thinkLevel for responsePrefix template interpolation. */ onModelSelected?: (ctx: ModelSelectedContext) => void; disableBlockStreaming?: boolean; /** Timeout for block reply delivery (ms). */ blockReplyTimeoutMs?: number; /** If provided, only load these skills for this session (empty = no skills). */ skillFilter?: string[]; /** Mutable ref to track if a reply was sent (for Slack "first" threading mode). */ hasRepliedRef?: { value: boolean; }; }; type ReplyPayload = { text?: string; mediaUrl?: string; mediaUrls?: string[]; replyToId?: string; replyToTag?: boolean; /** True when [[reply_to_current]] was present but not yet mapped to a message id. */ replyToCurrent?: boolean; /** Send audio as voice message (bubble) instead of audio file. Defaults to false. */ audioAsVoice?: boolean; isError?: boolean; /** Channel-specific payload data (per-channel envelope). */ channelData?: Record<string, unknown>; }; //#endregion //#region src/channels/chat-type.d.ts type NormalizedChatType = "direct" | "group" | "channel"; //#endregion //#region src/config/types.base.d.ts type TypingMode = "never" | "instant" | "thinking" | "message"; type SessionScope = "per-sender" | "global"; type DmScope = "main" | "per-peer" | "per-channel-peer" | "per-account-channel-peer"; type ReplyToMode = "off" | "first" | "all"; type GroupPolicy = "open" | "disabled" | "allowlist"; type DmPolicy = "pairing" | "allowlist" | "open" | "disabled"; type OutboundRetryConfig = { /** Max retry attempts for outbound requests (default: 3). */attempts?: number; /** Minimum retry delay in ms (default: 300-500ms depending on provider). */ minDelayMs?: number; /** Maximum retry delay cap in ms (default: 30000). */ maxDelayMs?: number; /** Jitter factor (0-1) applied to delays (default: 0.1). */ jitter?: number; }; type BlockStreamingCoalesceConfig = { minChars?: number; maxChars?: number; idleMs?: number; }; type BlockStreamingChunkConfig = { minChars?: number; maxChars?: number; breakPreference?: "paragraph" | "newline" | "sentence"; }; type MarkdownTableMode = "off" | "bullets" | "code"; type MarkdownConfig = { /** Table rendering mode (off|bullets|code). */tables?: MarkdownTableMode; }; type HumanDelayConfig = { /** Delay style for block replies (off|natural|custom). */mode?: "off" | "natural" | "custom"; /** Minimum delay in milliseconds (default: 800). */ minMs?: number; /** Maximum delay in milliseconds (default: 2500). */ maxMs?: number; }; type SessionSendPolicyAction = "allow" | "deny"; type SessionSendPolicyMatch = { channel?: string; chatType?: NormalizedChatType; keyPrefix?: string; }; type SessionSendPolicyRule = { action: SessionSendPolicyAction; match?: SessionSendPolicyMatch; }; type SessionSendPolicyConfig = { default?: SessionSendPolicyAction; rules?: SessionSendPolicyRule[]; }; type SessionResetMode = "daily" | "idle"; type SessionResetConfig = { mode?: SessionResetMode; /** Local hour (0-23) for the daily reset boundary. */ atHour?: number; /** Sliding idle window (minutes). When set with daily mode, whichever expires first wins. */ idleMinutes?: number; }; type SessionResetByTypeConfig = { dm?: SessionResetConfig; group?: SessionResetConfig; thread?: SessionResetConfig; }; type SessionConfig = { scope?: SessionScope; /** DM session scoping (default: "main"). */ dmScope?: DmScope; /** Map platform-prefixed identities (e.g. "telegram:123") to canonical DM peers. */ identityLinks?: Record<string, string[]>; resetTriggers?: string[]; idleMinutes?: number; reset?: SessionResetConfig; resetByType?: SessionResetByTypeConfig; /** Channel-specific reset overrides (e.g. { discord: { mode: "idle", idleMinutes: 10080 } }). */ resetByChannel?: Record<string, SessionResetConfig>; store?: string; typingIntervalSeconds?: number; typingMode?: TypingMode; mainKey?: string; sendPolicy?: SessionSendPolicyConfig; agentToAgent?: { /** Max ping-pong turns between requester/target (0–5). Default: 5. */maxPingPongTurns?: number; }; }; type LoggingConfig = { level?: "silent" | "fatal" | "error" | "warn" | "info" | "debug" | "trace"; file?: string; consoleLevel?: "silent" | "fatal" | "error" | "warn" | "info" | "debug" | "trace"; consoleStyle?: "pretty" | "compact" | "json"; /** Redact sensitive tokens in tool summaries. Default: "tools". */ redactSensitive?: "off" | "tools"; /** Regex patterns used to redact sensitive tokens (defaults apply when unset). */ redactPatterns?: string[]; }; type DiagnosticsOtelConfig = { enabled?: boolean; endpoint?: string; protocol?: "http/protobuf" | "grpc"; headers?: Record<string, string>; serviceName?: string; traces?: boolean; metrics?: boolean; logs?: boolean; /** Trace sample rate (0.0 - 1.0). */ sampleRate?: number; /** Metric export interval (ms). */ flushIntervalMs?: number; }; type DiagnosticsCacheTraceConfig = { enabled?: boolean; filePath?: string; includeMessages?: boolean; includePrompt?: boolean; includeSystem?: boolean; }; type DiagnosticsConfig = { enabled?: boolean; /** Optional ad-hoc diagnostics flags (e.g. "telegram.http"). */ flags?: string[]; otel?: DiagnosticsOtelConfig; cacheTrace?: DiagnosticsCacheTraceConfig; }; type WebReconnectConfig = { initialMs?: number; maxMs?: number; factor?: number; jitter?: number; maxAttempts?: number; }; type WebConfig = { /** If false, do not start the WhatsApp web provider. Default: true. */enabled?: boolean; heartbeatSeconds?: number; reconnect?: WebReconnectConfig; }; type AgentElevatedAllowFromConfig = Partial<Record<string, Array<string | number>>>; type IdentityConfig = { name?: string; theme?: string; emoji?: string; /** Avatar image: workspace-relative path, http(s) URL, or data URI. */ avatar?: string; }; //#endregion //#region src/config/types.sandbox.d.ts type SandboxDockerSettings = { /** Docker image to use for sandbox containers. */image?: string; /** Prefix for sandbox container names. */ containerPrefix?: string; /** Container workdir mount path (default: /workspace). */ workdir?: string; /** Run container rootfs read-only. */ readOnlyRoot?: boolean; /** Extra tmpfs mounts for read-only containers. */ tmpfs?: string[]; /** Container network mode (bridge|none|custom). */ network?: string; /** Container user (uid:gid). */ user?: string; /** Drop Linux capabilities. */ capDrop?: string[]; /** Extra environment variables for sandbox exec. */ env?: Record<string, string>; /** Optional setup command run once after container creation. */ setupCommand?: string; /** Limit container PIDs (0 = Docker default). */ pidsLimit?: number; /** Limit container memory (e.g. 512m, 2g, or bytes as number). */ memory?: string | number; /** Limit container memory swap (same format as memory). */ memorySwap?: string | number; /** Limit container CPU shares (e.g. 0.5, 1, 2). */ cpus?: number; /** * Set ulimit values by name (e.g. nofile, nproc). * Use "soft:hard" string, a number, or { soft, hard }. */ ulimits?: Record<string, string | number | { soft?: number; hard?: number; }>; /** Seccomp profile (path or profile name). */ seccompProfile?: string; /** AppArmor profile name. */ apparmorProfile?: string; /** DNS servers (e.g. ["1.1.1.1", "8.8.8.8"]). */ dns?: string[]; /** Extra host mappings (e.g. ["api.local:10.0.0.2"]). */ extraHosts?: string[]; /** Additional bind mounts (host:container:mode format, e.g. ["/host/path:/container/path:rw"]). */ binds?: string[]; }; type SandboxBrowserSettings = { enabled?: boolean; image?: string; containerPrefix?: string; cdpPort?: number; vncPort?: number; noVncPort?: number; headless?: boolean; enableNoVnc?: boolean; /** * Allow sandboxed sessions to target the host browser control server. * Default: false. */ allowHostControl?: boolean; /** * When true (default), sandboxed browser control will try to start/reattach to * the sandbox browser container when a tool call needs it. */ autoStart?: boolean; /** Max time to wait for CDP to become reachable after auto-start (ms). */ autoStartTimeoutMs?: number; }; type SandboxPruneSettings = { /** Prune if idle for more than N hours (0 disables). */idleHours?: number; /** Prune if older than N days (0 disables). */ maxAgeDays?: number; }; //#endregion //#region src/config/types.tools.d.ts type MediaUnderstandingScopeMatch = { channel?: string; chatType?: NormalizedChatType; keyPrefix?: string; }; type MediaUnderstandingScopeRule = { action: SessionSendPolicyAction; match?: MediaUnderstandingScopeMatch; }; type MediaUnderstandingScopeConfig = { default?: SessionSendPolicyAction; rules?: MediaUnderstandingScopeRule[]; }; type MediaUnderstandingCapability$1 = "image" | "audio" | "video"; type MediaUnderstandingAttachmentsConfig = { /** Select the first matching attachment or process multiple. */mode?: "first" | "all"; /** Max number of attachments to process (default: 1). */ maxAttachments?: number; /** Attachment ordering preference. */ prefer?: "first" | "last" | "path" | "url"; }; type MediaUnderstandingModelConfig = { /** provider API id (e.g. openai, google). */provider?: string; /** Model id for provider-based understanding. */ model?: string; /** Optional capability tags for shared model lists. */ capabilities?: MediaUnderstandingCapability$1[]; /** Use a CLI command instead of provider API. */ type?: "provider" | "cli"; /** CLI binary (required when type=cli). */ command?: string; /** CLI args (template-enabled). */ args?: string[]; /** Optional prompt override for this model entry. */ prompt?: string; /** Optional max output characters for this model entry. */ maxChars?: number; /** Optional max bytes for this model entry. */ maxBytes?: number; /** Optional timeout override (seconds) for this model entry. */ timeoutSeconds?: number; /** Optional language hint for audio transcription. */ language?: string; /** Optional provider-specific query params (merged into requests). */ providerOptions?: Record<string, Record<string, string | number | boolean>>; /** @deprecated Use providerOptions.deepgram instead. */ deepgram?: { detectLanguage?: boolean; punctuate?: boolean; smartFormat?: boolean; }; /** Optional base URL override for provider requests. */ baseUrl?: string; /** Optional headers merged into provider requests. */ headers?: Record<string, string>; /** Auth profile id to use for this provider. */ profile?: string; /** Preferred profile id if multiple are available. */ preferredProfile?: string; }; type MediaUnderstandingConfig = { /** Enable media understanding when models are configured. */enabled?: boolean; /** Optional scope gating for understanding. */ scope?: MediaUnderstandingScopeConfig; /** Default max bytes to send. */ maxBytes?: number; /** Default max output characters. */ maxChars?: number; /** Default prompt. */ prompt?: string; /** Default timeout (seconds). */ timeoutSeconds?: number; /** Default language hint (audio). */ language?: string; /** Optional provider-specific query params (merged into requests). */ providerOptions?: Record<string, Record<string, string | number | boolean>>; /** @deprecated Use providerOptions.deepgram instead. */ deepgram?: { detectLanguage?: boolean; punctuate?: boolean; smartFormat?: boolean; }; /** Optional base URL override for provider requests. */ baseUrl?: string; /** Optional headers merged into provider requests. */ headers?: Record<string, string>; /** Attachment selection policy. */ attachments?: MediaUnderstandingAttachmentsConfig; /** Ordered model list (fallbacks in order). */ models?: MediaUnderstandingModelConfig[]; }; type LinkModelConfig = { /** Use a CLI command for link processing. */type?: "cli"; /** CLI binary (required when type=cli). */ command: string; /** CLI args (template-enabled). */ args?: string[]; /** Optional timeout override (seconds) for this model entry. */ timeoutSeconds?: number; }; type LinkToolsConfig = { /** Enable link understanding when models are configured. */enabled?: boolean; /** Optional scope gating for understanding. */ scope?: MediaUnderstandingScopeConfig; /** Max number of links to process per message. */ maxLinks?: number; /** Default timeout (seconds). */ timeoutSeconds?: number; /** Ordered model list (fallbacks in order). */ models?: LinkModelConfig[]; }; type MediaToolsConfig = { /** Shared model list applied across image/audio/video. */models?: MediaUnderstandingModelConfig[]; /** Max concurrent media understanding runs. */ concurrency?: number; image?: MediaUnderstandingConfig; audio?: MediaUnderstandingConfig; video?: MediaUnderstandingConfig; }; type ToolProfileId = "minimal" | "coding" | "messaging" | "full"; type ToolPolicyConfig = { allow?: string[]; /** * Additional allowlist entries merged into the effective allowlist. * * Intended for additive configuration (e.g., "also allow lobster") without forcing * users to replace/duplicate an existing allowlist or profile. */ alsoAllow?: string[]; deny?: string[]; profile?: ToolProfileId; }; type GroupToolPolicyConfig = { allow?: string[]; /** Additional allowlist entries merged into allow. */ alsoAllow?: string[]; deny?: string[]; }; type GroupToolPolicyBySenderConfig = Record<string, GroupToolPolicyConfig>; type ExecToolConfig = { /** Exec host routing (default: sandbox). */host?: "sandbox" | "gateway" | "node"; /** Exec security mode (default: deny). */ security?: "deny" | "allowlist" | "full"; /** Exec ask mode (default: on-miss). */ ask?: "off" | "on-miss" | "always"; /** Default node binding for exec.host=node (node id/name). */ node?: string; /** Directories to prepend to PATH when running exec (gateway/sandbox). */ pathPrepend?: string[]; /** Safe stdin-only binaries that can run without allowlist entries. */ safeBins?: string[]; /** Default time (ms) before an exec command auto-backgrounds. */ backgroundMs?: number; /** Default timeout (seconds) before auto-killing exec commands. */ timeoutSec?: number; /** Emit a running notice (ms) when approval-backed exec runs long (default: 10000, 0 = off). */ approvalRunningNoticeMs?: number; /** How long to keep finished sessions in memory (ms). */ cleanupMs?: number; /** Emit a system event and heartbeat when a backgrounded exec exits. */ notifyOnExit?: boolean; /** apply_patch subtool configuration (experimental). */ applyPatch?: { /** Enable apply_patch for OpenAI models (default: false). */enabled?: boolean; /** * Optional allowlist of model ids that can use apply_patch. * Accepts either raw ids (e.g. "gpt-5.2") or full ids (e.g. "openai/gpt-5.2"). */ allowModels?: string[]; }; }; type AgentToolsConfig = { /** Base tool profile applied before allow/deny lists. */profile?: ToolProfileId; allow?: string[]; /** Additional allowlist entries merged into allow and/or profile allowlist. */ alsoAllow?: string[]; deny?: string[]; /** Optional tool policy overrides keyed by provider id or "provider/model". */ byProvider?: Record<string, ToolPolicyConfig>; /** Per-agent elevated exec gate (can only further restrict global tools.elevated). */ elevated?: { /** Enable or disable elevated mode for this agent (default: true). */enabled?: boolean; /** Approved senders for /elevated (per-provider allowlists). */ allowFrom?: AgentElevatedAllowFromConfig; }; /** Exec tool defaults for this agent. */ exec?: ExecToolConfig; sandbox?: { tools?: { allow?: string[]; deny?: string[]; }; }; }; type MemorySearchConfig = { /** Enable vector memory search (default: true). */enabled?: boolean; /** Sources to index and search (default: ["memory"]). */ sources?: Array<"memory" | "sessions">; /** Extra paths to include in memory search (directories or .md files). */ extraPaths?: string[]; /** Experimental memory search settings. */ experimental?: { /** Enable session transcript indexing (experimental, default: false). */sessionMemory?: boolean; }; /** Embedding provider mode. */ provider?: "openai" | "gemini" | "local"; remote?: { baseUrl?: string; apiKey?: string; headers?: Record<string, string>; batch?: { /** Enable batch API for embedding indexing (OpenAI/Gemini; default: true). */enabled?: boolean; /** Wait for batch completion (default: true). */ wait?: boolean; /** Max concurrent batch jobs (default: 2). */ concurrency?: number; /** Poll interval in ms (default: 5000). */ pollIntervalMs?: number; /** Timeout in minutes (default: 60). */ timeoutMinutes?: number; }; }; /** Fallback behavior when embeddings fail. */ fallback?: "openai" | "gemini" | "local" | "none"; /** Embedding model id (remote) or alias (local). */ model?: string; /** Local embedding settings (node-llama-cpp). */ local?: { /** GGUF model path or hf: URI. */modelPath?: string; /** Optional cache directory for local models. */ modelCacheDir?: string; }; /** Index storage configuration. */ store?: { driver?: "sqlite"; path?: string; vector?: { /** Enable sqlite-vec extension for vector search (default: true). */enabled?: boolean; /** Optional override path to sqlite-vec extension (.dylib/.so/.dll). */ extensionPath?: string; }; cache?: { /** Enable embedding cache (default: true). */enabled?: boolean; /** Optional max cache entries per provider/model. */ maxEntries?: number; }; }; /** Chunking configuration. */ chunking?: { tokens?: number; overlap?: number; }; /** Sync behavior. */ sync?: { onSessionStart?: boolean; onSearch?: boolean; watch?: boolean; watchDebounceMs?: number; intervalMinutes?: number; sessions?: { /** Minimum appended bytes before session transcripts are reindexed. */deltaBytes?: number; /** Minimum appended JSONL lines before session transcripts are reindexed. */ deltaMessages?: number; }; }; /** Query behavior. */ query?: { maxResults?: number; minScore?: number; hybrid?: { /** Enable hybrid BM25 + vector search (default: true). */enabled?: boolean; /** Weight for vector similarity when merging results (0-1). */ vectorWeight?: number; /** Weight for BM25 text relevance when merging results (0-1). */ textWeight?: number; /** Multiplier for candidate pool size (default: 4). */ candidateMultiplier?: number; }; }; /** Index cache behavior. */ cache?: { /** Cache chunk embeddings in SQLite (default: true). */enabled?: boolean; /** Optional cap on cached embeddings (best-effort). */ maxEntries?: number; }; }; type ToolsConfig = { /** Base tool profile applied before allow/deny lists. */profile?: ToolProfileId; allow?: string[]; /** Additional allowlist entries merged into allow and/or profile allowlist. */ alsoAllow?: string[]; deny?: string[]; /** Optional tool policy overrides keyed by provider id or "provider/model". */ byProvider?: Record<string, ToolPolicyConfig>; web?: { search?: { /** Enable web search tool (default: true when API key is present). */enabled?: boolean; /** Search provider ("brave" or "perplexity"). */ provider?: "brave" | "perplexity"; /** Brave Search API key (optional; defaults to BRAVE_API_KEY env var). */ apiKey?: string; /** Default search results count (1-10). */ maxResults?: number; /** Timeout in seconds for search requests. */ timeoutSeconds?: number; /** Cache TTL in minutes for search results. */ cacheTtlMinutes?: number; /** Perplexity-specific configuration (used when provider="perplexity"). */ perplexity?: { /** API key for Perplexity or OpenRouter (defaults to PERPLEXITY_API_KEY or OPENROUTER_API_KEY env var). */apiKey?: string; /** Base URL for API requests (defaults to OpenRouter: https://openrouter.ai/api/v1). */ baseUrl?: string; /** Model to use (defaults to "perplexity/sonar-pro"). */ model?: string; }; }; fetch?: { /** Enable web fetch tool (default: true). */enabled?: boolean; /** Max characters to return from fetched content. */ maxChars?: number; /** Hard cap for maxChars (tool or config), defaults to 50000. */ maxCharsCap?: number; /** Timeout in seconds for fetch requests. */ timeoutSeconds?: number; /** Cache TTL in minutes for fetched content. */ cacheTtlMinutes?: number; /** Maximum number of redirects to follow (default: 3). */ maxRedirects?: number; /** Override User-Agent header for fetch requests. */ userAgent?: string; /** Use Readability to extract main content (default: true). */ readability?: boolean; firecrawl?: { /** Enable Firecrawl fallback (default: true when apiKey is set). */enabled?: boolean; /** Firecrawl API key (optional; defaults to FIRECRAWL_API_KEY env var). */ apiKey?: string; /** Firecrawl base URL (default: https://api.firecrawl.dev). */ baseUrl?: string; /** Whether to keep only main content (default: true). */ onlyMainContent?: boolean; /** Max age (ms) for cached Firecrawl content. */ maxAgeMs?: number; /** Timeout in seconds for Firecrawl requests. */ timeoutSeconds?: number; }; }; }; media?: MediaToolsConfig; links?: LinkToolsConfig; /** Message tool configuration. */ message?: { /** * @deprecated Use tools.message.crossContext settings. * Allows cross-context sends across providers. */ allowCrossContextSend?: boolean; crossContext?: { /** Allow sends to other channels within the same provider (default: true). */allowWithinProvider?: boolean; /** Allow sends across different providers (default: false). */ allowAcrossProviders?: boolean; /** Cross-context marker configuration. */ marker?: { /** Enable origin markers for cross-context sends (default: true). */enabled?: boolean; /** Text prefix template, supports {channel}. */ prefix?: string; /** Text suffix template, supports {channel}. */ suffix?: string; }; }; broadcast?: { /** Enable broadcast action (default: true). */enabled?: boolean; }; }; agentToAgent?: { /** Enable agent-to-agent messaging tools. Default: false. */enabled?: boolean; /** Allowlist of agent ids or patterns (implementation-defined). */ allow?: string[]; }; /** Elevated exec permissions for the host machine. */ elevated?: { /** Enable or disable elevated mode (default: true). */enabled?: boolean; /** Approved senders for /elevated (per-provider allowlists). */ allowFrom?: AgentElevatedAllowFromConfig; }; /** Exec tool defaults. */ exec?: ExecToolConfig; /** Sub-agent tool policy defaults (deny wins). */ subagents?: { /** Default model selection for spawned sub-agents (string or {primary,fallbacks}). */model?: string | { primary?: string; fallbacks?: string[]; }; tools?: { allow?: string[]; deny?: string[]; }; }; /** Sandbox tool policy defaults (deny wins). */ sandbox?: { tools?: { allow?: string[]; deny?: string[]; }; }; }; //#endregion //#region src/config/types.agent-defaults.d.ts type AgentModelEntryConfig = { alias?: string; /** Provider-specific API parameters (e.g., GLM-4.7 thinking mode). */ params?: Record<string, unknown>; }; type AgentModelListConfig = { primary?: string; fallbacks?: string[]; }; type AgentContextPruningConfig = { mode?: "off" | "cache-ttl"; /** TTL to consider cache expired (duration string, default unit: minutes). */ ttl?: string; keepLastAssistants?: number; softTrimRatio?: number; hardClearRatio?: number; minPrunableToolChars?: number; tools?: { allow?: string[]; deny?: string[]; }; softTrim?: { maxChars?: number; headChars?: number; tailChars?: number; }; hardClear?: { enabled?: boolean; placeholder?: string; }; }; type CliBackendConfig = { /** CLI command to execute (absolute path or on PATH). */command: string; /** Base args applied to every invocation. */ args?: string[]; /** Output parsing mode (default: json). */ output?: "json" | "text" | "jsonl"; /** Output parsing mode when resuming a CLI session. */ resumeOutput?: "json" | "text" | "jsonl"; /** Prompt input mode (default: arg). */ input?: "arg" | "stdin"; /** Max prompt length for arg mode (if exceeded, stdin is used). */ maxPromptArgChars?: number; /** Extra env vars injected for this CLI. */ env?: Record<string, string>; /** Env vars to remove before launching this CLI. */ clearEnv?: string[]; /** Flag used to pass model id (e.g. --model). */ modelArg?: string; /** Model aliases mapping (config model id → CLI model id). */ modelAliases?: Record<string, string>; /** Flag used to pass session id (e.g. --session-id). */ sessionArg?: string; /** Extra args used when resuming a session (use {sessionId} placeholder). */ sessionArgs?: string[]; /** Alternate args to use when resuming a session (use {sessionId} placeholder). */ resumeArgs?: string[]; /** When to pass session ids. */ sessionMode?: "always" | "existing" | "none"; /** JSON fields to read session id from (in order). */ sessionIdFields?: string[]; /** Flag used to pass system prompt. */ systemPromptArg?: string; /** System prompt behavior (append vs replace). */ systemPromptMode?: "append" | "replace"; /** When to send system prompt. */ systemPromptWhen?: "first" | "always" | "never"; /** Flag used to pass image paths. */ imageArg?: string; /** How to pass multiple images. */ imageMode?: "repeat" | "list"; /** Serialize runs for this CLI. */ serialize?: boolean; }; type AgentDefaultsConfig = { /** Primary model and fallbacks (provider/model). */model?: AgentModelListConfig; /** Optional image-capable model and fallbacks (provider/model). */ imageModel?: AgentModelListConfig; /** Model catalog with optional aliases (full provider/model keys). */ models?: Record<string, AgentModelEntryConfig>; /** Agent working directory (preferred). Used as the default cwd for agent runs. */ workspace?: string; /** Optional repository root for system prompt runtime line (overrides auto-detect). */ repoRoot?: string; /** Skip bootstrap (BOOTSTRAP.md creation, etc.) for pre-configured deployments. */ skipBootstrap?: boolean; /** Max chars for injected bootstrap files before truncation (default: 20000). */ bootstrapMaxChars?: number; /** Optional IANA timezone for the user (used in system prompt; defaults to host timezone). */ userTimezone?: string; /** Time format in system prompt: auto (OS preference), 12-hour, or 24-hour. */ timeFormat?: "auto" | "12" | "24"; /** * Envelope timestamp timezone: "utc" (default), "local", "user", or an IANA timezone string. */ envelopeTimezone?: string; /** * Include absolute timestamps in message envelopes ("on" | "off", default: "on"). */ envelopeTimestamp?: "on" | "off"; /** * Include elapsed time in message envelopes ("on" | "off", default: "on"). */ envelopeElapsed?: "on" | "off"; /** Optional context window cap (used for runtime estimates + status %). */ contextTokens?: number; /** Optional CLI backends for text-only fallback (claude-cli, etc.). */ cliBackends?: Record<string, CliBackendConfig>; /** Opt-in: prune old tool results from the LLM context to reduce token usage. */ contextPruning?: AgentContextPruningConfig; /** Compaction tuning and pre-compaction memory flush behavior. */ compaction?: AgentCompactionConfig; /** Vector memory search configuration (per-agent overrides supported). */ memorySearch?: MemorySearchConfig; /** Default thinking level when no /think directive is present. */ thinkingDefault?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh"; /** Default verbose level when no /verbose directive is present. */ verboseDefault?: "off" | "on" | "full"; /** Default elevated level when no /elevated directive is present. */ elevatedDefault?: "off" | "on" | "ask" | "full"; /** Default block streaming level when no override is present. */ blockStreamingDefault?: "off" | "on"; /** * Block streaming boundary: * - "text_end": end of each assistant text content block (before tool calls) * - "message_end": end of the whole assistant message (may include tool blocks) */ blockStreamingBreak?: "text_end" | "message_end"; /** Soft block chunking for streamed replies (min/max chars, prefer paragraph/newline). */ blockStreamingChunk?: BlockStreamingChunkConfig; /** * Block reply coalescing (merge streamed chunks before send). * idleMs: wait time before flushing when idle. */ blockStreamingCoalesce?: BlockStreamingCoalesceConfig; /** Human-like delay between block replies. */ humanDelay?: HumanDelayConfig; timeoutSeconds?: number; /** Max inbound media size in MB for agent-visible attachments (text note or future image attach). */ mediaMaxMb?: number; typingIntervalSeconds?: number; /** Typing indicator start mode (never|instant|thinking|message). */ typingMode?: TypingMode; /** Periodic background heartbeat runs. */ heartbeat?: { /** Heartbeat interval (duration string, default unit: minutes; default: 30m). */every?: string; /** Optional active-hours window (local time); heartbeats run only inside this window. */ activeHours?: { /** Start time (24h, HH:MM). Inclusive. */start?: string; /** End time (24h, HH:MM). Exclusive. Use "24:00" for end-of-day. */ end?: string; /** Timezone for the window ("user", "local", or IANA TZ id). Default: "user". */ timezone?: string; }; /** Heartbeat model override (provider/model). */ model?: string; /** Session key for heartbeat runs ("main" or explicit session key). */ session?: string; /** Delivery target ("last", "none", or a channel id). */ target?: "last" | "none" | ChannelId; /** Optional delivery override (E.164 for WhatsApp, chat id for Telegram). */ to?: string; /** Override the heartbeat prompt body (default: "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK."). */ prompt?: string; /** Max chars allowed after HEARTBEAT_OK before delivery (default: 30). */ ackMaxChars?: number; /** * When enabled, deliver the model's reasoning payload for heartbeat runs (when available) * as a separate message prefixed with `Reasoning:` (same as `/reasoning on`). * * Default: false (only the final heartbeat payload is delivered). */ includeReasoning?: boolean; }; /** Max concurrent agent runs across all conversations. Default: 1 (sequential). */ maxConcurrent?: number; /** Sub-agent defaults (spawned via sessions_spawn). */ subagents?: { /** Max concurrent sub-agent runs (global lane: "subagent"). Default: 1. */maxConcurrent?: number; /** Auto-archive sub-agent sessions after N minutes (default: 60). */ archiveAfterMinutes?: number; /** Default model selection for spawned sub-agents (string or {primary,fallbacks}). */ model?: string | { primary?: string; fallbacks?: string[]; }; /** Default thinking level for spawned sub-agents (e.g. "off", "low", "medium", "high"). */ thinking?: string; }; /** Optional sandbox settings for non-main sessions. */ sandbox?: { /** Enable sandboxing for sessions. */mode?: "off" | "non-main" | "all"; /** * Agent workspace access inside the sandbox. * - "none": do not mount the agent workspace into the container; use a sandbox workspace under workspaceRoot * - "ro": mount the agent workspace read-only; disables write/edit tools * - "rw": mount the agent workspace read/write; enables write/edit tools */ workspaceAccess?: "none" | "ro" | "rw"; /** * Session tools visibility for sandboxed sessions. * - "spawned": only allow session tools to target sessions spawned from this session (default) * - "all": allow session tools to target any session */ sessionToolsVisibility?: "spawned" | "all"; /** Container/workspace scope for sandbox isolation. */ scope?: "session" | "agent" | "shared"; /** Legacy alias for scope ("session" when true, "shared" when false). */ perSession?: boolean; /** Root directory for sandbox workspaces. */ workspaceRoot?: string; /** Docker-specific sandbox settings. */ docker?: SandboxDockerSettings; /** Optional sandboxed browser settings. */ browser?: SandboxBrowserSettings; /** Auto-prune sandbox containers. */ prune?: SandboxPruneSettings; }; }; type AgentCompactionMode = "default" | "safeguard"; type AgentCompactionConfig = { /** Compaction summarization mode. */mode?: AgentCompactionMode; /** Minimum reserve tokens enforced for Pi compaction (0 disables the floor). */ reserveTokensFloor?: number; /** Max share of context window for history during safeguard pruning (0.1–0.9, default 0.5). */ maxHistoryShare?: number; /** Pre-compaction memory flush (agentic turn). Default: enabled. */ memoryFlush?: AgentCompactionMemoryFlushConfig; }; type AgentCompactionMemoryFlushConfig = { /** Enable the pre-compaction memory flush (default: true). */enabled?: boolean; /** Run the memory flush when context is within this many tokens of the compaction threshold. */ softThresholdTokens?: number; /** User prompt used for the memory flush turn (NO_REPLY is enforced if missing). */ prompt?: string; /** System prompt appended for the memory flush turn. */ systemPrompt?: string; }; //#endregion //#region src/config/types.queue.d.ts type QueueMode = "steer" | "followup" | "collect" | "steer-backlog" | "steer+backlog" | "queue" | "interrupt"; type QueueDropPolicy = "old" | "new" | "summarize"; type QueueModeByProvider = { whatsapp?: QueueMode; telegram?: QueueMode; discord?: QueueMode; googlechat?: QueueMode; slack?: QueueMode; signal?: QueueMode; imessage?: QueueMode; msteams?: QueueMode; webchat?: QueueMode; }; //#endregion //#region src/config/types.tts.d.ts type TtsProvider = "elevenlabs" | "openai" | "edge"; type TtsMode = "final" | "all"; type TtsAutoMode = "off" | "always" | "inbound" | "tagged"; type TtsModelOverrideConfig = { /** Enable model-provided overrides for TTS. */enabled?: boolean; /** Allow model-provided TTS text blocks. */ allowText?: boolean; /** Allow model-provided provider override. */ allowProvider?: boolean; /** Allow model-provided voice/voiceId override. */ allowVoice?: boolean; /** Allow model-provided modelId override. */ allowModelId?: boolean; /** Allow model-provided voice settings override. */ allowVoiceSettings?: boolean; /** Allow model-provided normalization or language overrides. */ allowNormalization?: boolean; /** Allow model-provided seed override. */ allowSeed?: boolean; }; type TtsConfig = { /** Auto-TTS mode (preferred). */auto?: TtsAutoMode; /** Legacy: enable auto-TTS when `auto` is not set. */ enabled?: boolean; /** Apply TTS to final replies only or to all replies (tool/block/final). */ mode?: TtsMode; /** Primary TTS provider (fallbacks are automatic). */ provider?: TtsProvider; /** Optional model override for TTS auto-summary (provider/model or alias). */ summaryModel?: string; /** Allow the model to override TTS parameters. */ modelOverrides?: TtsModelOverrideConfig; /** ElevenLabs configuration. */ elevenlabs?: { apiKey?: string; baseUrl?: string; voiceId?: string; modelId?: string; seed?: number; applyTextNormalization?: "auto" | "on" | "off"; languageCode?: string; voiceSettings?: { stability?: number; similarityBoost?: number; style?: number; useSpeakerBoost?: boolean; speed?: number; }; }; /** OpenAI configuration. */ openai?: { apiKey?: string; model?: string; voice?: string; }; /** Microsoft Edge (node-edge-tts) configuration. */ edge?: { /** Explicitly allow Edge TTS usage (no API key required). */enabled?: boolean; voice?: string; lang?: string; outputFormat?: string; pitch?: string; rate?: string; volume?: string; saveSubtitles?: boolean; proxy?: string; timeoutMs?: number; }; /** Optional path for local TTS user preferences JSON. */ prefsPath?: string; /** Hard cap for text sent to TTS (chars). */ maxTextLength?: number; /** API request timeout (ms). */ timeoutMs?: number; }; //#endregion //#region src/config/types.messages.d.ts type GroupChatConfig = { mentionPatterns?: string[]; historyLimit?: number; }; type DmConfig = { historyLimit?: number; }; type QueueConfig = { mode?: QueueMode; byChannel?: QueueModeByProvider; debounceMs?: number; /** Per-channel debounce overrides (ms). */ debounceMsByChannel?: InboundDebounceByProvider; cap?: number; drop?: QueueDropPolicy; }; type InboundDebounceByProvider = Record<string, number>; type InboundDebounceConfig = { debounceMs?: number; byChannel?: InboundDebounceByProvider; }; type BroadcastStrategy = "parallel" | "sequential"; type BroadcastConfig = { /** Default processing strategy for broadcast peers. */strategy?: BroadcastStrategy; /** * Map peer IDs to arrays of agent IDs that should ALL process messages. * * Note: the index signature includes `undefined` so `strategy?: ...` remains type-safe. */ [peerId: string]: string[] | BroadcastStrategy | undefined; }; type AudioConfig = { /** @deprecated Use tools.media.audio.models instead. */transcription?: { command: string[]; timeoutSeconds?: number; }; }; type MessagesConfig = { /** @deprecated Use `whatsapp.messagePrefix` (WhatsApp-only inbound prefix). */messagePrefix?: string; /** * Prefix auto-added to all outbound replies. * * - string: explicit prefix (may include template variables) * - special value: `"auto"` derives `[{agents.list[].identity.name}]` for the routed agent (when set) * * Supported template variables (case-insensitive): * - `{model}` - short model name (e.g., `claude-opus-4-5`, `gpt-4o`) * - `{modelFull}` - full model identifier (e.g., `anthropic/claude-opus-4-5`) * - `{provider}` - provider name (e.g., `anthropic`, `openai`) * - `{thinkingLevel}` or `{think}` - current thinking level (`high`, `low`, `off`) * - `{identity.name}` or `{identityName}` - agent identity name * * Example: `"[{model} | think:{thinkingLevel}]"` → `"[claude-opus-4-5 | think:high]"` * * Unresolved variables remain as literal text (e.g., `{model}` if context unavailable). * * Default: none */ responsePrefix?: string; groupChat?: GroupChatConfig; queue?: QueueConfig; /** Debounce rapid inbound messages per sender (global + per-channel overrides). */ inbound?: InboundDebounceConfig; /** Emoji reaction used to acknowledge inbound messages (empty disables). */ ackReaction?: string; /** When to send ack reactions. Default: "group-mentions". */ ackReactionScope?: "group-mentions" | "group-all" | "direct" | "all"; /** Remove ack reaction after reply is sent (default: false). */ removeAckAfterReply?: boolean; /** Text-to-speech settings for outbound replies. */ tts?: TtsConfig; }; type NativeCommandsSetting = boolean | "auto"; type CommandsConfig = { /** Enable native command registration when supported (default: "auto"). */native?: NativeCommandsSetting; /** Enable native skill command registration when supported (default: "auto"). */ nativeSkills?: NativeCommandsSetting; /** Enable text command parsing (default: true). */ text?: boolean; /** Allow bash chat command (`!`; `/bash` alias) (default: false). */ bash?: boolean; /** How long bash waits before backgrounding (default: 2000; 0 backgrounds immediately). */ bashForegroundMs?: number; /** Allow /config command (default: false). */ config?: boolean; /** Allow /debug command (default: false). */ debug?: boolean; /** Allow restart commands/tools (default: false). */ restart?: boolean; /** Enforce access-group allowlists/policies for commands (default: true). */ useAccessGroups?: boolean; }; type ProviderCommandsConfig = { /** Override native command registration for this provider (bool or "auto"). */native?: NativeCommandsSetting; /** Override native skill command registration for this provider (bool or "auto"). */ nativeSkills?: NativeCommandsSetting; }; //#endregion //#region src/config/types.agents.d.ts type AgentModelConfig = string | { /** Primary model (provider/model). */primary?: string; /** Per-agent model fallbacks (provider/model). */ fallbacks?: string[]; }; type AgentConfig = { id: string; default?: boolean; name?: string; workspace?: string; agentDir?: string; model?: AgentModelConfig; /** Optional allowlist of skills for this agent (omit = all skills; empty = none). */ skills?: string[]; memorySearch?: MemorySearchConfig; /** Human-like delay between block replies for this agent. */ humanDelay?: HumanDelayConfig; /** Optional per-agent heartbeat overrides. */ heartbeat?: AgentDefaultsConfig["heartbeat"]; identity?: IdentityConfig; groupChat?: GroupChatConfig; subagents?: { /** Allow spawning sub-agents under other agent ids. Use "*" to allow any. */allowAgents?: string[]; /** Per-agent default model for spawned sub-agents (string or {primary,fallbacks}). */ model?: string | { primary?: string; fallbacks?: string[]; }; }; sandbox?: { mode?: "off" | "non-main" | "all"; /** Agent workspace access inside the sandbox. */ workspaceAccess?: "none" | "ro" | "rw"; /** * Session tools visibility for sandboxed sessions. * - "spawned": only allow session tools to target sessions spawned from this session (default) * - "all": allow session tools to target any session */ sessionToolsVisibility?: "spawned" | "all"; /** Container/workspace scope for sandbox isolation. */ scope?: "session" | "agent" | "shared"; /** Legacy alias for scope ("session" when true, "shared" when false). */ perSession?: boolean; workspaceRoot?: string; /** Docker-specific sandbox overrides for this agent. */ docker?: SandboxDockerSettings; /** Optional sandboxed browser overrides for this agent. */ browser?: SandboxBrowserSettings; /** Auto-prune overrides for this agent. */ prune?: SandboxPruneSettings; }; tools?: AgentToolsConfig; }; type AgentsConfig = { defaults?: AgentDefaultsConfig; list?: AgentConfig[]; }; type AgentBinding = { agentId: string; match: { channel: string; accountId?: string; peer?: { kind: "dm" | "group" | "channel"; id: string; }; guildId?: string; teamId?: string; }; }; //#endregion //#region src/config/types.approvals.d.ts type ExecApprovalForwardingMode = "session" | "targets" | "both"; type ExecApprovalForwardTarget = { /** Channel id (e.g. "discord", "slack", or plugin channel id). */channel: string; /** Destination id (channel id, user id, etc. depending on channel). */ to: string; /** Optional account id for multi-account channels. */ accountId?: string; /** Optional thread id to reply inside a thread. */ threadId?: string | number; }; type ExecApprovalForwardingConfig = { /** Enable forwarding exec approvals to chat channels. Default: false. */enabled?: boolean; /** Delivery mode (session=origin chat, targets=config targets, both=both). Default: session. */ mode?: ExecApprovalForwardingMode; /** Only forward approvals for these agent IDs. Omit = all agents. */ agentFilter?: string[]; /** Only forward approvals matching these session key patterns (substring or regex). */ sessionFilter?: string[]; /** Explicit delivery targets (used when mode includes targets). */ targets?: ExecApprovalForwardTarget[]; }; type ApprovalsConfig = { exec?: ExecApprovalForwardingConfig; }; //#endregion //#region src/config/types.auth.d.ts type AuthProfileConfig = { provider: string; /** * Credential type expected in auth-profiles.json for this profile id. * - api_key: static provider API key * - oauth: refreshable OAuth crede