openclaw/src/config/types.ts

1843 lines
60 KiB
TypeScript
Raw Normal View History

2026-01-04 07:05:04 +01:00
export type ReplyMode = "text" | "command";
2026-01-07 21:58:54 +00:00
export type TypingMode = "never" | "instant" | "thinking" | "message";
2026-01-04 07:05:04 +01:00
export type SessionScope = "per-sender" | "global";
export type ReplyToMode = "off" | "first" | "all";
2026-01-06 06:40:42 +00:00
export type GroupPolicy = "open" | "disabled" | "allowlist";
export type DmPolicy = "pairing" | "allowlist" | "open" | "disabled";
2026-01-04 07:05:04 +01:00
2026-01-07 17:48:19 +00:00
export 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;
};
export type BlockStreamingCoalesceConfig = {
minChars?: number;
maxChars?: number;
idleMs?: number;
};
export type BlockStreamingChunkConfig = {
minChars?: number;
maxChars?: number;
breakPreference?: "paragraph" | "newline" | "sentence";
};
export 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;
};
2026-01-04 07:05:04 +01:00
export type SessionSendPolicyAction = "allow" | "deny";
export type SessionSendPolicyMatch = {
channel?: string;
2026-01-04 07:05:04 +01:00
chatType?: "direct" | "group" | "room";
keyPrefix?: string;
};
export type SessionSendPolicyRule = {
action: SessionSendPolicyAction;
match?: SessionSendPolicyMatch;
};
export type SessionSendPolicyConfig = {
default?: SessionSendPolicyAction;
rules?: SessionSendPolicyRule[];
};
export type SessionConfig = {
scope?: SessionScope;
resetTriggers?: string[];
idleMinutes?: number;
heartbeatIdleMinutes?: number;
store?: string;
typingIntervalSeconds?: number;
typingMode?: TypingMode;
2026-01-04 07:05:04 +01:00
mainKey?: string;
sendPolicy?: SessionSendPolicyConfig;
agentToAgent?: {
/** Max ping-pong turns between requester/target (05). Default: 5. */
maxPingPongTurns?: number;
};
};
export 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[];
2026-01-04 07:05:04 +01:00
};
export type WebReconnectConfig = {
initialMs?: number;
maxMs?: number;
factor?: number;
jitter?: number;
maxAttempts?: number; // 0 = unlimited
};
export type WebConfig = {
/** If false, do not start the WhatsApp web provider. Default: true. */
enabled?: boolean;
heartbeatSeconds?: number;
reconnect?: WebReconnectConfig;
};
Move provider to a plugin-architecture (#661) * refactor: introduce provider plugin registry * refactor: move provider CLI to plugins * docs: add provider plugin implementation notes * refactor: shift provider runtime logic into plugins * refactor: add plugin defaults and summaries * docs: update provider plugin notes * feat(commands): add /commands slash list * Auto-reply: tidy help message * Auto-reply: fix status command lint * Tests: align google shared expectations * Auto-reply: tidy help message * Auto-reply: fix status command lint * refactor: move provider routing into plugins * test: align agent routing expectations * docs: update provider plugin notes * refactor: route replies via provider plugins * docs: note route-reply plugin hooks * refactor: extend provider plugin contract * refactor: derive provider status from plugins * refactor: unify gateway provider control * refactor: use plugin metadata in auto-reply * fix: parenthesize cron target selection * refactor: derive gateway methods from plugins * refactor: generalize provider logout * refactor: route provider logout through plugins * refactor: move WhatsApp web login methods into plugin * refactor: generalize provider log prefixes * refactor: centralize default chat provider * refactor: derive provider lists from registry * refactor: move provider reload noops into plugins * refactor: resolve web login provider via alias * refactor: derive CLI provider options from plugins * refactor: derive prompt provider list from plugins * style: apply biome lint fixes * fix: resolve provider routing edge cases * docs: update provider plugin refactor notes * fix(gateway): harden agent provider routing * refactor: move provider routing into plugins * refactor: move provider CLI to plugins * refactor: derive provider lists from registry * fix: restore slash command parsing * refactor: align provider ids for schema * refactor: unify outbound target resolution * fix: keep outbound labels stable * feat: add msteams to cron surfaces * fix: clean up lint build issues * refactor: localize chat provider alias normalization * refactor: drive gateway provider lists from plugins * docs: update provider plugin notes * style: format message-provider * fix: avoid provider registry init cycles * style: sort message-provider imports * fix: relax provider alias map typing * refactor: move provider routing into plugins * refactor: add plugin pairing/config adapters * refactor: route pairing and provider removal via plugins * refactor: align auto-reply provider typing * test: stabilize telegram media mocks * docs: update provider plugin refactor notes * refactor: pluginize outbound targets * refactor: pluginize provider selection * refactor: generalize text chunk limits * docs: update provider plugin notes * refactor: generalize group session/config * fix: normalize provider id for room detection * fix: avoid provider init in system prompt * style: formatting cleanup * refactor: normalize agent delivery targets * test: update outbound delivery labels * chore: fix lint regressions * refactor: extend provider plugin adapters * refactor: move elevated/block streaming defaults to plugins * refactor: defer outbound send deps to plugins * docs: note plugin-driven streaming/elevated defaults * refactor: centralize webchat provider constant * refactor: add provider setup adapters * refactor: delegate provider add config to plugins * docs: document plugin-driven provider add * refactor: add plugin state/binding metadata * refactor: build agent provider status from plugins * docs: note plugin-driven agent bindings * refactor: centralize internal provider constant usage * fix: normalize WhatsApp targets for groups and E.164 (#631) (thanks @imfing) * refactor: centralize default chat provider * refactor: centralize WhatsApp target normalization * refactor: move provider routing into plugins * refactor: normalize agent delivery targets * chore: fix lint regressions * fix: normalize WhatsApp targets for groups and E.164 (#631) (thanks @imfing) * feat: expand provider plugin adapters * refactor: route auto-reply via provider plugins * fix: align WhatsApp target normalization * fix: normalize WhatsApp targets for groups and E.164 (#631) (thanks @imfing) * refactor: centralize WhatsApp target normalization * feat: add /config chat config updates * docs: add /config get alias * feat(commands): add /commands slash list * refactor: centralize default chat provider * style: apply biome lint fixes * chore: fix lint regressions * fix: clean up whatsapp allowlist typing * style: format config command helpers * refactor: pluginize tool threading context * refactor: normalize session announce targets * docs: note new plugin threading and announce hooks * refactor: pluginize message actions * docs: update provider plugin actions notes * fix: align provider action adapters * refactor: centralize webchat checks * style: format message provider helpers * refactor: move provider onboarding into adapters * docs: note onboarding provider adapters * feat: add msteams onboarding adapter * style: organize onboarding imports * fix: normalize msteams allowFrom types * feat: add plugin text chunk limits * refactor: use plugin chunk limit fallbacks * feat: add provider mention stripping hooks * style: organize provider plugin type imports * refactor: generalize health snapshots * refactor: update macOS health snapshot handling * docs: refresh health snapshot notes * style: format health snapshot updates * refactor: drive security warnings via plugins * docs: note provider security adapter * style: format provider security adapters * refactor: centralize provider account defaults * refactor: type gateway client identity constants * chore: regen gateway protocol swift * fix: degrade health on failed provider probe * refactor: centralize pairing approve hint * docs: add plugin CLI command references * refactor: route auth and tool sends through plugins * docs: expand provider plugin hooks * refactor: document provider docking touchpoints * refactor: normalize internal provider defaults * refactor: streamline outbound delivery wiring * refactor: make provider onboarding plugin-owned * refactor: support provider-owned agent tools * refactor: move telegram draft chunking into telegram module * refactor: infer provider tool sends via extractToolSend * fix: repair plugin onboarding imports * refactor: de-dup outbound target normalization * style: tidy plugin and agent imports * refactor: data-drive provider selection line * fix: satisfy lint after provider plugin rebase * test: deflake gateway-cli coverage * style: format gateway-cli coverage test * refactor(provider-plugins): simplify provider ids * test(pairing-cli): avoid provider-specific ternary * style(macos): swiftformat HealthStore * refactor(sandbox): derive provider tool denylist * fix(sandbox): avoid plugin init in defaults * refactor(provider-plugins): centralize provider aliases * style(test): satisfy biome * refactor(protocol): v3 providers.status maps * refactor(ui): adapt to protocol v3 * refactor(macos): adapt to protocol v3 * test: update providers.status v3 fixtures * refactor(gateway): map provider runtime snapshot * test(gateway): update reload runtime snapshot * refactor(whatsapp): normalize heartbeat provider id * docs(refactor): update provider plugin notes * style: satisfy biome after rebase * fix: describe sandboxed elevated in prompt * feat(gateway): add agent image attachments + live probe * refactor: derive CLI provider options from plugins * fix(gateway): harden agent provider routing * fix(gateway): harden agent provider routing * refactor: align provider ids for schema * fix(protocol): keep agent provider string * fix(gateway): harden agent provider routing * fix(protocol): keep agent provider string * refactor: normalize agent delivery targets * refactor: support provider-owned agent tools * refactor(config): provider-keyed elevated allowFrom * style: satisfy biome * fix(gateway): appease provider narrowing * style: satisfy biome * refactor(reply): move group intro hints into plugin * fix(reply): avoid plugin registry init cycle * refactor(providers): add lightweight provider dock * refactor(gateway): use typed client id in connect * refactor(providers): document docks and avoid init cycles * refactor(providers): make media limit helper generic * fix(providers): break plugin registry import cycles * style: satisfy biome * refactor(status-all): build providers table from plugins * refactor(gateway): delegate web login to provider plugin * refactor(provider): drop web alias * refactor(provider): lazy-load monitors * style: satisfy lint/format * style: format status-all providers table * style: swiftformat gateway discovery model * test: make reload plan plugin-driven * fix: avoid token stringification in status-all * refactor: make provider IDs explicit in status * feat: warn on signal/imessage provider runtime errors * test: cover gateway provider runtime warnings in status * fix: add runtime kind to provider status issues * test: cover health degradation on probe failure * fix: keep routeReply lightweight * style: organize routeReply imports * refactor(web): extract auth-store helpers * refactor(whatsapp): lazy login imports * refactor(outbound): route replies via plugin outbound * docs: update provider plugin notes * style: format provider status issues * fix: make sandbox scope warning wrap-safe * refactor: load outbound adapters from provider plugins * docs: update provider plugin outbound notes * style(macos): fix swiftformat lint * docs: changelog for provider plugins * fix(macos): satisfy swiftformat * fix(macos): open settings via menu action * style: format after rebase * fix(macos): open Settings via menu action --------- Co-authored-by: LK <luke@kyohere.com> Co-authored-by: Luke K (pr-0f3t) <2609441+lc0rp@users.noreply.github.com> Co-authored-by: Xin <xin@imfing.com>
2026-01-11 11:45:25 +00:00
// Provider docking: allowlists keyed by provider id (and internal "webchat").
export type AgentElevatedAllowFromConfig = Partial<
Record<string, Array<string | number>>
>;
2026-01-04 07:05:04 +01:00
export type IdentityConfig = {
name?: string;
theme?: string;
emoji?: string;
};
export type WhatsAppActionConfig = {
reactions?: boolean;
sendMessage?: boolean;
polls?: boolean;
};
2026-01-04 07:05:04 +01:00
export type WhatsAppConfig = {
2026-01-06 08:40:21 +00:00
/** Optional per-account WhatsApp configuration (multi-account). */
accounts?: Record<string, WhatsAppAccountConfig>;
/** Optional provider capability tags used for agent/runtime guidance. */
capabilities?: string[];
/**
* Inbound message prefix (WhatsApp only).
* Default: `[{agents.list[].identity.name}]` (or `[clawdbot]`) when allowFrom is empty, else `""`.
*/
messagePrefix?: string;
/** Direct message access policy (default: pairing). */
dmPolicy?: DmPolicy;
/**
* Same-phone setup (bot uses your personal WhatsApp number).
*/
selfChatMode?: boolean;
2026-01-04 07:05:04 +01:00
/** Optional allowlist for WhatsApp direct chats (E.164). */
allowFrom?: string[];
2026-01-06 06:40:42 +00:00
/** Optional allowlist for WhatsApp group senders (E.164). */
groupAllowFrom?: string[];
/**
* Controls how group messages are handled:
2026-01-12 08:21:50 +00:00
* - "open": groups bypass allowFrom, only mention-gating applies
* - "disabled": block all group messages entirely
2026-01-06 06:40:42 +00:00
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
*/
2026-01-06 06:40:42 +00:00
groupPolicy?: GroupPolicy;
2026-01-10 18:53:33 +01:00
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
/** Max DM turns to keep as history context. */
dmHistoryLimit?: number;
/** Per-DM config overrides keyed by user ID. */
dms?: Record<string, DmConfig>;
2026-01-04 07:05:04 +01:00
/** Outbound text chunk size (chars). Default: 4000. */
textChunkLimit?: number;
/** Maximum media file size in MB. Default: 50. */
mediaMaxMb?: number;
/** Disable block streaming for this account. */
blockStreaming?: boolean;
/** Merge streamed block replies before sending. */
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
/** Per-action tool gating (default: true for all). */
actions?: WhatsAppActionConfig;
2026-01-04 07:05:04 +01:00
groups?: Record<
string,
{
requireMention?: boolean;
}
>;
/** Acknowledgment reaction sent immediately upon message receipt. */
ackReaction?: {
/** Emoji to use for acknowledgment (e.g., "👀"). Empty = disabled. */
emoji?: string;
/** Send reactions in direct chats. Default: true. */
direct?: boolean;
/**
* Send reactions in group chats:
* - "always": react to all group messages
* - "mentions": react only when bot is mentioned
* - "never": never react in groups
* Default: "mentions"
*/
group?: "always" | "mentions" | "never";
};
2026-01-04 07:05:04 +01:00
};
2026-01-06 08:40:21 +00:00
export type WhatsAppAccountConfig = {
/** Optional display name for this account (used in CLI/UI lists). */
name?: string;
/** Optional provider capability tags used for agent/runtime guidance. */
capabilities?: string[];
2026-01-06 08:40:21 +00:00
/** If false, do not start this WhatsApp account provider. Default: true. */
enabled?: boolean;
/** Inbound message prefix override for this account (WhatsApp only). */
messagePrefix?: string;
2026-01-06 08:40:21 +00:00
/** Override auth directory (Baileys multi-file auth state). */
authDir?: string;
/** Direct message access policy (default: pairing). */
dmPolicy?: DmPolicy;
/** Same-phone setup for this account (bot uses your personal WhatsApp number). */
selfChatMode?: boolean;
2026-01-06 08:40:21 +00:00
allowFrom?: string[];
groupAllowFrom?: string[];
groupPolicy?: GroupPolicy;
2026-01-10 18:53:33 +01:00
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
/** Max DM turns to keep as history context. */
dmHistoryLimit?: number;
/** Per-DM config overrides keyed by user ID. */
dms?: Record<string, DmConfig>;
2026-01-06 08:40:21 +00:00
textChunkLimit?: number;
mediaMaxMb?: number;
blockStreaming?: boolean;
/** Merge streamed block replies before sending. */
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
2026-01-06 08:40:21 +00:00
groups?: Record<
string,
{
requireMention?: boolean;
}
>;
/** Acknowledgment reaction sent immediately upon message receipt. */
ackReaction?: {
/** Emoji to use for acknowledgment (e.g., "👀"). Empty = disabled. */
emoji?: string;
/** Send reactions in direct chats. Default: true. */
direct?: boolean;
/**
* Send reactions in group chats:
* - "always": react to all group messages
* - "mentions": react only when bot is mentioned
* - "never": never react in groups
* Default: "mentions"
*/
group?: "always" | "mentions" | "never";
};
2026-01-06 08:40:21 +00:00
};
2026-01-04 07:05:04 +01:00
export type BrowserProfileConfig = {
/** CDP port for this profile. Allocated once at creation, persisted permanently. */
cdpPort?: number;
/** CDP URL for this profile (use for remote Chrome). */
cdpUrl?: string;
/** Profile color (hex). Auto-assigned at creation. */
color: string;
};
export type BrowserConfig = {
enabled?: boolean;
/** Base URL of the clawd browser control server. Default: http://127.0.0.1:18791 */
controlUrl?: string;
/** Base URL of the CDP endpoint. Default: controlUrl with port + 1. */
cdpUrl?: string;
/** Accent color for the clawd browser profile (hex). Default: #FF4500 */
color?: string;
/** Override the browser executable path (macOS/Linux). */
executablePath?: string;
/** Start Chrome headless (best-effort). Default: false */
headless?: boolean;
/** Pass --no-sandbox to Chrome (Linux containers). Default: false */
noSandbox?: boolean;
/** If true: never launch; only attach to an existing browser. Default: false */
attachOnly?: boolean;
/** Default profile to use when profile param is omitted. Default: "clawd" */
defaultProfile?: string;
/** Named browser profiles with explicit CDP ports or URLs. */
profiles?: Record<string, BrowserProfileConfig>;
};
export type CronConfig = {
enabled?: boolean;
store?: string;
maxConcurrentRuns?: number;
};
export type HookMappingMatch = {
path?: string;
source?: string;
};
export type HookMappingTransform = {
module: string;
export?: string;
};
export type HookMappingConfig = {
id?: string;
match?: HookMappingMatch;
action?: "wake" | "agent";
wakeMode?: "now" | "next-heartbeat";
name?: string;
sessionKey?: string;
messageTemplate?: string;
textTemplate?: string;
deliver?: boolean;
channel?:
2026-01-04 07:05:04 +01:00
| "last"
| "whatsapp"
| "telegram"
| "discord"
2026-01-03 23:12:11 -06:00
| "slack"
2026-01-04 07:05:04 +01:00
| "signal"
| "imessage"
| "msteams";
2026-01-04 07:05:04 +01:00
to?: string;
2026-01-08 09:33:27 +00:00
/** Override model for this hook (provider/model or alias). */
model?: string;
2026-01-04 07:05:04 +01:00
thinking?: string;
timeoutSeconds?: number;
transform?: HookMappingTransform;
};
export type HooksGmailTailscaleMode = "off" | "serve" | "funnel";
export type HooksGmailConfig = {
account?: string;
label?: string;
topic?: string;
subscription?: string;
pushToken?: string;
hookUrl?: string;
includeBody?: boolean;
maxBytes?: number;
renewEveryMinutes?: number;
serve?: {
bind?: string;
port?: number;
path?: string;
};
tailscale?: {
mode?: HooksGmailTailscaleMode;
path?: string;
/** Optional tailscale serve/funnel target (port, host:port, or full URL). */
target?: string;
2026-01-04 07:05:04 +01:00
};
/** Optional model override for Gmail hook processing (provider/model or alias). */
model?: string;
/** Optional thinking level override for Gmail hook processing. */
thinking?: "off" | "minimal" | "low" | "medium" | "high";
2026-01-04 07:05:04 +01:00
};
export type HooksConfig = {
enabled?: boolean;
path?: string;
token?: string;
maxBodyBytes?: number;
presets?: string[];
transformsDir?: string;
mappings?: HookMappingConfig[];
gmail?: HooksGmailConfig;
};
2026-01-07 04:10:13 +01:00
export type TelegramActionConfig = {
reactions?: boolean;
sendMessage?: boolean;
2026-01-07 04:10:13 +01:00
};
export type TelegramAccountConfig = {
/** Optional display name for this account (used in CLI/UI lists). */
name?: string;
/** Optional provider capability tags used for agent/runtime guidance. */
capabilities?: string[];
2026-01-12 21:49:44 +00:00
/** Override native command registration for Telegram (bool or "auto"). */
commands?: ProviderCommandsConfig;
/**
* Controls how Telegram direct chats (DMs) are handled:
* - "pairing" (default): unknown senders get a pairing code; owner must approve
* - "allowlist": only allow senders in allowFrom (or paired allow store)
* - "open": allow all inbound DMs (requires allowFrom to include "*")
* - "disabled": ignore all inbound DMs
*/
dmPolicy?: DmPolicy;
/** If false, do not start this Telegram account. Default: true. */
2026-01-04 07:05:04 +01:00
enabled?: boolean;
botToken?: string;
/** Path to file containing bot token (for secret managers like agenix). */
2026-01-04 07:05:04 +01:00
tokenFile?: string;
/** Control reply threading when reply tags are present (off|first|all). */
replyToMode?: ReplyToMode;
groups?: Record<string, TelegramGroupConfig>;
2026-01-04 07:05:04 +01:00
allowFrom?: Array<string | number>;
2026-01-06 06:40:42 +00:00
/** Optional allowlist for Telegram group senders (user ids or usernames). */
groupAllowFrom?: Array<string | number>;
/**
* Controls how group messages are handled:
2026-01-12 08:21:50 +00:00
* - "open": groups bypass allowFrom, only mention-gating applies
* - "disabled": block all group messages entirely
2026-01-06 06:40:42 +00:00
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
*/
2026-01-06 06:40:42 +00:00
groupPolicy?: GroupPolicy;
2026-01-10 18:53:33 +01:00
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
/** Max DM turns to keep as history context. */
dmHistoryLimit?: number;
/** Per-DM config overrides keyed by user ID. */
dms?: Record<string, DmConfig>;
2026-01-04 07:05:04 +01:00
/** Outbound text chunk size (chars). Default: 4000. */
textChunkLimit?: number;
/** Disable block streaming for this account. */
blockStreaming?: boolean;
/** Chunking config for draft streaming in `streamMode: "block"`. */
draftChunk?: BlockStreamingChunkConfig;
/** Merge streamed block replies before sending. */
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
2026-01-07 11:08:11 +01:00
/** Draft streaming mode for Telegram (off|partial|block). Default: partial. */
streamMode?: "off" | "partial" | "block";
2026-01-04 07:05:04 +01:00
mediaMaxMb?: number;
2026-01-07 17:48:19 +00:00
/** Retry policy for outbound Telegram API calls. */
retry?: OutboundRetryConfig;
2026-01-04 07:05:04 +01:00
proxy?: string;
webhookUrl?: string;
webhookSecret?: string;
webhookPath?: string;
2026-01-07 04:10:13 +01:00
/** Per-action tool gating (default: true for all). */
actions?: TelegramActionConfig;
2026-01-04 07:05:04 +01:00
};
export type TelegramTopicConfig = {
requireMention?: boolean;
/** If specified, only load these skills for this topic. Omit = all skills; empty = no skills. */
skills?: string[];
/** If false, disable the bot for this topic. */
enabled?: boolean;
/** Optional allowlist for topic senders (ids or usernames). */
allowFrom?: Array<string | number>;
/** Optional system prompt snippet for this topic. */
systemPrompt?: string;
};
export type TelegramGroupConfig = {
requireMention?: boolean;
/** If specified, only load these skills for this group (when no topic). Omit = all skills; empty = no skills. */
skills?: string[];
/** Per-topic configuration (key is message_thread_id as string) */
topics?: Record<string, TelegramTopicConfig>;
/** If false, disable the bot for this group (and its topics). */
enabled?: boolean;
/** Optional allowlist for group senders (ids or usernames). */
allowFrom?: Array<string | number>;
/** Optional system prompt snippet for this group. */
systemPrompt?: string;
};
export type TelegramConfig = {
/** Optional per-account Telegram configuration (multi-account). */
accounts?: Record<string, TelegramAccountConfig>;
} & TelegramAccountConfig;
2026-01-04 07:05:04 +01:00
export type DiscordDmConfig = {
/** If false, ignore all incoming Discord DMs. Default: true. */
enabled?: boolean;
/** Direct message access policy (default: pairing). */
policy?: DmPolicy;
2026-01-04 07:05:04 +01:00
/** Allowlist for DM senders (ids or names). */
allowFrom?: Array<string | number>;
/** If true, allow group DMs (default: false). */
groupEnabled?: boolean;
/** Optional allowlist for group DM channels (ids or slugs). */
groupChannels?: Array<string | number>;
};
export type DiscordGuildChannelConfig = {
allow?: boolean;
requireMention?: boolean;
/** If specified, only load these skills for this channel. Omit = all skills; empty = no skills. */
skills?: string[];
/** If false, disable the bot for this channel. */
enabled?: boolean;
/** Optional allowlist for channel senders (ids or names). */
users?: Array<string | number>;
/** Optional system prompt snippet for this channel. */
systemPrompt?: string;
2026-01-04 07:05:04 +01:00
};
export type DiscordReactionNotificationMode =
| "off"
| "own"
| "all"
| "allowlist";
export type DiscordGuildEntry = {
slug?: string;
requireMention?: boolean;
/** Reaction notification mode (off|own|all|allowlist). Default: own. */
reactionNotifications?: DiscordReactionNotificationMode;
users?: Array<string | number>;
channels?: Record<string, DiscordGuildChannelConfig>;
};
export type DiscordActionConfig = {
reactions?: boolean;
stickers?: boolean;
polls?: boolean;
permissions?: boolean;
messages?: boolean;
threads?: boolean;
pins?: boolean;
search?: boolean;
memberInfo?: boolean;
roleInfo?: boolean;
roles?: boolean;
channelInfo?: boolean;
voiceStatus?: boolean;
events?: boolean;
moderation?: boolean;
emojiUploads?: boolean;
stickerUploads?: boolean;
channels?: boolean;
2026-01-04 07:05:04 +01:00
};
export type DiscordAccountConfig = {
/** Optional display name for this account (used in CLI/UI lists). */
name?: string;
/** Optional provider capability tags used for agent/runtime guidance. */
capabilities?: string[];
2026-01-12 21:49:44 +00:00
/** Override native command registration for Discord (bool or "auto"). */
commands?: ProviderCommandsConfig;
/** If false, do not start this Discord account. Default: true. */
2026-01-04 07:05:04 +01:00
enabled?: boolean;
token?: string;
/** Allow bot-authored messages to trigger replies (default: false). */
allowBots?: boolean;
2026-01-06 06:40:42 +00:00
/**
* Controls how guild channel messages are handled:
2026-01-12 08:21:50 +00:00
* - "open": guild channels bypass allowlists; mention-gating applies
2026-01-06 06:40:42 +00:00
* - "disabled": block all guild channel messages
* - "allowlist": only allow channels present in discord.guilds.*.channels
*/
groupPolicy?: GroupPolicy;
2026-01-04 07:05:04 +01:00
/** Outbound text chunk size (chars). Default: 2000. */
textChunkLimit?: number;
/** Disable block streaming for this account. */
blockStreaming?: boolean;
/** Merge streamed block replies before sending. */
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
2026-01-08 04:02:04 +01:00
/**
* Soft max line count per Discord message.
* Discord clients can clip/collapse very tall messages; splitting by lines
* keeps replies readable in-channel. Default: 17.
*/
maxLinesPerMessage?: number;
2026-01-04 07:05:04 +01:00
mediaMaxMb?: number;
historyLimit?: number;
/** Max DM turns to keep as history context. */
dmHistoryLimit?: number;
/** Per-DM config overrides keyed by user ID. */
dms?: Record<string, DmConfig>;
2026-01-07 17:48:19 +00:00
/** Retry policy for outbound Discord API calls. */
retry?: OutboundRetryConfig;
2026-01-04 07:05:04 +01:00
/** Per-action tool gating (default: true for all). */
actions?: DiscordActionConfig;
/** Control reply threading when reply tags are present (off|first|all). */
replyToMode?: ReplyToMode;
dm?: DiscordDmConfig;
/** New per-guild config keyed by guild id or slug. */
guilds?: Record<string, DiscordGuildEntry>;
};
export type DiscordConfig = {
/** Optional per-account Discord configuration (multi-account). */
accounts?: Record<string, DiscordAccountConfig>;
} & DiscordAccountConfig;
2026-01-03 23:12:11 -06:00
export type SlackDmConfig = {
/** If false, ignore all incoming Slack DMs. Default: true. */
enabled?: boolean;
/** Direct message access policy (default: pairing). */
policy?: DmPolicy;
2026-01-03 23:12:11 -06:00
/** Allowlist for DM senders (ids). */
allowFrom?: Array<string | number>;
/** If true, allow group DMs (default: false). */
groupEnabled?: boolean;
/** Optional allowlist for group DM channels (ids or slugs). */
groupChannels?: Array<string | number>;
};
export type SlackChannelConfig = {
/** If false, disable the bot in this channel. (Alias for allow: false.) */
enabled?: boolean;
/** Legacy channel allow toggle; prefer enabled. */
2026-01-03 23:12:11 -06:00
allow?: boolean;
/** Require mentioning the bot to trigger replies. */
2026-01-03 23:12:11 -06:00
requireMention?: boolean;
2026-01-08 08:49:16 +01:00
/** Allow bot-authored messages to trigger replies (default: false). */
allowBots?: boolean;
/** Allowlist of users that can invoke the bot in this channel. */
users?: Array<string | number>;
/** Optional skill filter for this channel. */
skills?: string[];
/** Optional system prompt for this channel. */
systemPrompt?: string;
2026-01-03 23:12:11 -06:00
};
export type SignalReactionNotificationMode =
| "off"
| "own"
| "all"
| "allowlist";
2026-01-03 23:12:11 -06:00
export type SlackReactionNotificationMode = "off" | "own" | "all" | "allowlist";
export type SlackActionConfig = {
reactions?: boolean;
messages?: boolean;
pins?: boolean;
search?: boolean;
permissions?: boolean;
memberInfo?: boolean;
channelInfo?: boolean;
emojiList?: boolean;
};
export type SlackSlashCommandConfig = {
/** Enable handling for the configured slash command (default: false). */
enabled?: boolean;
/** Slash command name (default: "clawd"). */
name?: string;
/** Session key prefix for slash commands (default: "slack:slash"). */
sessionPrefix?: string;
/** Reply ephemerally (default: true). */
ephemeral?: boolean;
};
export type SlackAccountConfig = {
/** Optional display name for this account (used in CLI/UI lists). */
name?: string;
/** Optional provider capability tags used for agent/runtime guidance. */
capabilities?: string[];
2026-01-12 21:49:44 +00:00
/** Override native command registration for Slack (bool or "auto"). */
commands?: ProviderCommandsConfig;
/** If false, do not start this Slack account. Default: true. */
2026-01-03 23:12:11 -06:00
enabled?: boolean;
botToken?: string;
appToken?: string;
2026-01-08 08:49:16 +01:00
/** Allow bot-authored messages to trigger replies (default: false). */
allowBots?: boolean;
2026-01-06 06:40:42 +00:00
/**
* Controls how channel messages are handled:
2026-01-12 08:21:50 +00:00
* - "open": channels bypass allowlists; mention-gating applies
2026-01-06 06:40:42 +00:00
* - "disabled": block all channel messages
* - "allowlist": only allow channels present in channels.slack.channels
2026-01-06 06:40:42 +00:00
*/
groupPolicy?: GroupPolicy;
2026-01-10 18:53:33 +01:00
/** Max channel messages to keep as history context (0 disables). */
historyLimit?: number;
/** Max DM turns to keep as history context. */
dmHistoryLimit?: number;
/** Per-DM config overrides keyed by user ID. */
dms?: Record<string, DmConfig>;
2026-01-03 23:12:11 -06:00
textChunkLimit?: number;
blockStreaming?: boolean;
/** Merge streamed block replies before sending. */
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
2026-01-03 23:12:11 -06:00
mediaMaxMb?: number;
/** Reaction notification mode (off|own|all|allowlist). Default: own. */
reactionNotifications?: SlackReactionNotificationMode;
/** Allowlist for reaction notifications when mode is allowlist. */
reactionAllowlist?: Array<string | number>;
/** Control reply threading when reply tags are present (off|first|all). */
replyToMode?: ReplyToMode;
2026-01-03 23:12:11 -06:00
actions?: SlackActionConfig;
slashCommand?: SlackSlashCommandConfig;
dm?: SlackDmConfig;
channels?: Record<string, SlackChannelConfig>;
};
export type SlackConfig = {
/** Optional per-account Slack configuration (multi-account). */
accounts?: Record<string, SlackAccountConfig>;
} & SlackAccountConfig;
export type SignalAccountConfig = {
/** Optional display name for this account (used in CLI/UI lists). */
name?: string;
/** Optional provider capability tags used for agent/runtime guidance. */
capabilities?: string[];
/** If false, do not start this Signal account. Default: true. */
2026-01-04 07:05:04 +01:00
enabled?: boolean;
/** Optional explicit E.164 account for signal-cli. */
account?: string;
/** Optional full base URL for signal-cli HTTP daemon. */
httpUrl?: string;
/** HTTP host for signal-cli daemon (default 127.0.0.1). */
httpHost?: string;
/** HTTP port for signal-cli daemon (default 8080). */
httpPort?: number;
/** signal-cli binary path (default: signal-cli). */
cliPath?: string;
/** Auto-start signal-cli daemon (default: true if httpUrl not set). */
autoStart?: boolean;
receiveMode?: "on-start" | "manual";
ignoreAttachments?: boolean;
ignoreStories?: boolean;
sendReadReceipts?: boolean;
/** Direct message access policy (default: pairing). */
dmPolicy?: DmPolicy;
2026-01-04 07:05:04 +01:00
allowFrom?: Array<string | number>;
2026-01-06 06:40:42 +00:00
/** Optional allowlist for Signal group senders (E.164). */
groupAllowFrom?: Array<string | number>;
/**
* Controls how group messages are handled:
2026-01-12 08:21:50 +00:00
* - "open": groups bypass allowFrom, no extra gating
2026-01-06 06:40:42 +00:00
* - "disabled": block all group messages
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
*/
groupPolicy?: GroupPolicy;
2026-01-10 18:53:33 +01:00
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
/** Max DM turns to keep as history context. */
dmHistoryLimit?: number;
/** Per-DM config overrides keyed by user ID. */
dms?: Record<string, DmConfig>;
2026-01-04 07:05:04 +01:00
/** Outbound text chunk size (chars). Default: 4000. */
textChunkLimit?: number;
blockStreaming?: boolean;
/** Merge streamed block replies before sending. */
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
2026-01-04 07:05:04 +01:00
mediaMaxMb?: number;
/** Reaction notification mode (off|own|all|allowlist). Default: own. */
reactionNotifications?: SignalReactionNotificationMode;
/** Allowlist for reaction notifications when mode is allowlist. */
reactionAllowlist?: Array<string | number>;
2026-01-04 07:05:04 +01:00
};
export type SignalConfig = {
/** Optional per-account Signal configuration (multi-account). */
accounts?: Record<string, SignalAccountConfig>;
} & SignalAccountConfig;
export type MSTeamsWebhookConfig = {
/** Port for the webhook server. Default: 3978. */
port?: number;
/** Path for the messages endpoint. Default: /api/messages. */
path?: string;
};
/** Reply style for MS Teams messages. */
export type MSTeamsReplyStyle = "thread" | "top-level";
/** Channel-level config for MS Teams. */
export type MSTeamsChannelConfig = {
/** Require @mention to respond. Default: true. */
requireMention?: boolean;
/** Reply style: "thread" replies to the message, "top-level" posts a new message. */
replyStyle?: MSTeamsReplyStyle;
};
/** Team-level config for MS Teams. */
export type MSTeamsTeamConfig = {
/** Default requireMention for channels in this team. */
requireMention?: boolean;
/** Default reply style for channels in this team. */
replyStyle?: MSTeamsReplyStyle;
/** Per-channel overrides. Key is conversation ID (e.g., "19:...@thread.tacv2"). */
channels?: Record<string, MSTeamsChannelConfig>;
};
export type MSTeamsConfig = {
/** If false, do not start the MS Teams provider. Default: true. */
enabled?: boolean;
/** Optional provider capability tags used for agent/runtime guidance. */
capabilities?: string[];
/** Azure Bot App ID (from Azure Bot registration). */
appId?: string;
/** Azure Bot App Password / Client Secret. */
appPassword?: string;
/** Azure AD Tenant ID (for single-tenant bots). */
tenantId?: string;
/** Webhook server configuration. */
webhook?: MSTeamsWebhookConfig;
/** Direct message access policy (default: pairing). */
dmPolicy?: DmPolicy;
/** Allowlist for DM senders (AAD object IDs or UPNs). */
allowFrom?: Array<string>;
2026-01-12 08:31:59 +00:00
/** Optional allowlist for group/channel senders (AAD object IDs or UPNs). */
groupAllowFrom?: Array<string>;
/**
* Controls how group/channel messages are handled:
* - "open": groups bypass allowFrom; mention-gating applies
* - "disabled": block all group messages
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
*/
groupPolicy?: GroupPolicy;
/** Outbound text chunk size (chars). Default: 4000. */
textChunkLimit?: number;
/** Merge streamed block replies before sending. */
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
/**
* Allowed host suffixes for inbound attachment downloads.
* Use ["*"] to allow any host (not recommended).
*/
mediaAllowHosts?: Array<string>;
/** Default: require @mention to respond in channels/groups. */
requireMention?: boolean;
2026-01-10 18:53:33 +01:00
/** Max group/channel messages to keep as history context (0 disables). */
historyLimit?: number;
/** Max DM turns to keep as history context. */
dmHistoryLimit?: number;
/** Per-DM config overrides keyed by user ID. */
dms?: Record<string, DmConfig>;
/** Default reply style: "thread" replies to the message, "top-level" posts a new message. */
replyStyle?: MSTeamsReplyStyle;
/** Per-team config. Key is team ID (from the /team/ URL path segment). */
teams?: Record<string, MSTeamsTeamConfig>;
};
export type ChannelsConfig = {
whatsapp?: WhatsAppConfig;
telegram?: TelegramConfig;
discord?: DiscordConfig;
slack?: SlackConfig;
signal?: SignalConfig;
imessage?: IMessageConfig;
msteams?: MSTeamsConfig;
};
export type IMessageAccountConfig = {
/** Optional display name for this account (used in CLI/UI lists). */
name?: string;
/** Optional provider capability tags used for agent/runtime guidance. */
capabilities?: string[];
/** If false, do not start this iMessage account. Default: true. */
2026-01-04 07:05:04 +01:00
enabled?: boolean;
/** imsg CLI binary path (default: imsg). */
cliPath?: string;
/** Optional Messages db path override. */
dbPath?: string;
/** Optional default send service (imessage|sms|auto). */
service?: "imessage" | "sms" | "auto";
/** Optional default region (used when sending SMS). */
region?: string;
/** Direct message access policy (default: pairing). */
dmPolicy?: DmPolicy;
2026-01-04 07:05:04 +01:00
/** Optional allowlist for inbound handles or chat_id targets. */
allowFrom?: Array<string | number>;
2026-01-06 06:40:42 +00:00
/** Optional allowlist for group senders or chat_id targets. */
groupAllowFrom?: Array<string | number>;
/**
* Controls how group messages are handled:
2026-01-12 08:21:50 +00:00
* - "open": groups bypass allowFrom; mention-gating applies
2026-01-06 06:40:42 +00:00
* - "disabled": block all group messages entirely
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
*/
groupPolicy?: GroupPolicy;
2026-01-10 18:53:33 +01:00
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
/** Max DM turns to keep as history context. */
dmHistoryLimit?: number;
/** Per-DM config overrides keyed by user ID. */
dms?: Record<string, DmConfig>;
2026-01-04 07:05:04 +01:00
/** Include attachments + reactions in watch payloads. */
includeAttachments?: boolean;
/** Max outbound media size in MB. */
mediaMaxMb?: number;
/** Outbound text chunk size (chars). Default: 4000. */
textChunkLimit?: number;
blockStreaming?: boolean;
/** Merge streamed block replies before sending. */
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
2026-01-04 07:05:04 +01:00
groups?: Record<
string,
{
requireMention?: boolean;
}
>;
};
export type IMessageConfig = {
/** Optional per-account iMessage configuration (multi-account). */
accounts?: Record<string, IMessageAccountConfig>;
} & IMessageAccountConfig;
2026-01-04 07:05:04 +01:00
export type QueueMode =
| "steer"
| "followup"
| "collect"
| "steer-backlog"
| "steer+backlog"
| "queue"
| "interrupt";
export type QueueDropPolicy = "old" | "new" | "summarize";
export type QueueModeByProvider = {
2026-01-04 07:05:04 +01:00
whatsapp?: QueueMode;
telegram?: QueueMode;
discord?: QueueMode;
2026-01-03 23:12:11 -06:00
slack?: QueueMode;
2026-01-04 07:05:04 +01:00
signal?: QueueMode;
imessage?: QueueMode;
msteams?: QueueMode;
2026-01-04 07:05:04 +01:00
webchat?: QueueMode;
};
export 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[];
2026-01-12 10:13:32 -07:00
/** Additional bind mounts (host:container:mode format, e.g. ["/host/path:/container/path:rw"]). */
binds?: string[];
};
export 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;
/**
* Allowlist of exact control URLs for target="custom".
* When set, any custom controlUrl must match this list.
*/
allowedControlUrls?: string[];
/**
* Allowlist of hostnames for control URLs (hostname only, no ports).
* When set, controlUrl hostname must match.
*/
allowedControlHosts?: string[];
/**
* Allowlist of ports for control URLs.
* When set, controlUrl port must match (defaults: http=80, https=443).
*/
allowedControlPorts?: number[];
2026-01-10 02:06:05 +00:00
/**
* 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;
};
export type SandboxPruneSettings = {
/** Prune if idle for more than N hours (0 disables). */
idleHours?: number;
/** Prune if older than N days (0 disables). */
maxAgeDays?: number;
};
2026-01-04 07:05:04 +01:00
export type GroupChatConfig = {
mentionPatterns?: string[];
historyLimit?: number;
};
export type DmConfig = {
historyLimit?: number;
};
export type QueueConfig = {
mode?: QueueMode;
byChannel?: QueueModeByProvider;
debounceMs?: number;
cap?: number;
drop?: QueueDropPolicy;
};
export type ToolProfileId = "minimal" | "coding" | "messaging" | "full";
export type AgentToolsConfig = {
/** Base tool profile applied before allow/deny lists. */
profile?: ToolProfileId;
allow?: string[];
deny?: string[];
/** Per-agent elevated exec gate (can only further restrict global tools.elevated). */
2026-01-09 20:42:16 +00:00
elevated?: {
/** Enable or disable elevated mode for this agent (default: true). */
enabled?: boolean;
/** Approved senders for /elevated (per-provider allowlists). */
allowFrom?: AgentElevatedAllowFromConfig;
};
sandbox?: {
tools?: {
allow?: string[];
deny?: string[];
};
2026-01-04 07:05:04 +01:00
};
};
2026-01-12 11:22:56 +00:00
export type MemorySearchConfig = {
/** Enable vector memory search (default: true). */
enabled?: boolean;
/** Embedding provider mode. */
provider?: "openai" | "local";
remote?: {
baseUrl?: string;
apiKey?: string;
headers?: Record<string, string>;
};
2026-01-12 11:22:56 +00:00
/** Fallback behavior when local embeddings fail. */
fallback?: "openai" | "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;
};
/** Chunking configuration. */
chunking?: {
tokens?: number;
overlap?: number;
};
/** Sync behavior. */
sync?: {
onSessionStart?: boolean;
onSearch?: boolean;
watch?: boolean;
watchDebounceMs?: number;
intervalMinutes?: number;
};
/** Query behavior. */
query?: {
maxResults?: number;
minScore?: number;
};
};
export type ToolsConfig = {
/** Base tool profile applied before allow/deny lists. */
profile?: ToolProfileId;
allow?: string[];
deny?: string[];
audio?: {
transcription?: {
/** CLI args (template-enabled). */
args?: string[];
timeoutSeconds?: number;
};
};
2026-01-06 08:40:21 +00:00
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?: {
/** Default time (ms) before an exec command auto-backgrounds. */
backgroundMs?: number;
/** Default timeout (seconds) before auto-killing exec commands. */
timeoutSec?: number;
/** How long to keep finished sessions in memory (ms). */
cleanupMs?: number;
/** 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[];
};
};
/** @deprecated Use tools.exec. */
bash?: {
/** Default time (ms) before a bash command auto-backgrounds. */
backgroundMs?: number;
/** Default timeout (seconds) before auto-killing bash commands. */
timeoutSec?: number;
/** How long to keep finished sessions in memory (ms). */
cleanupMs?: number;
};
/** Sub-agent tool policy defaults (deny wins). */
subagents?: {
2026-01-12 18:08:16 +00:00
/** Default model selection for spawned sub-agents (string or {primary,fallbacks}). */
model?: string | { primary?: string; fallbacks?: string[] };
tools?: {
allow?: string[];
deny?: string[];
2026-01-06 08:40:21 +00:00
};
};
/** Sandbox tool policy defaults (deny wins). */
sandbox?: {
tools?: {
allow?: string[];
deny?: string[];
};
};
};
export type AgentModelConfig =
| string
| {
/** Primary model (provider/model). */
primary?: string;
/** Per-agent model fallbacks (provider/model). */
fallbacks?: string[];
};
export type AgentConfig = {
id: string;
default?: boolean;
name?: string;
workspace?: string;
agentDir?: string;
model?: AgentModelConfig;
2026-01-12 11:22:56 +00:00
memorySearch?: MemorySearchConfig;
/** Human-like delay between block replies for this agent. */
humanDelay?: HumanDelayConfig;
identity?: IdentityConfig;
groupChat?: GroupChatConfig;
subagents?: {
/** Allow spawning sub-agents under other agent ids. Use "*" to allow any. */
allowAgents?: string[];
2026-01-12 18:08:16 +00:00
/** 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;
};
export type AgentsConfig = {
defaults?: AgentDefaultsConfig;
list?: AgentConfig[];
};
export type AgentBinding = {
agentId: string;
match: {
channel: string;
accountId?: string;
peer?: { kind: "dm" | "group" | "channel"; id: string };
guildId?: string;
teamId?: string;
};
};
export type BroadcastStrategy = "parallel" | "sequential";
export 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;
};
export type AudioConfig = {
/** @deprecated Use tools.audio.transcription instead. */
transcription?: {
// Optional CLI to turn inbound audio into text; templated args, must output transcript to stdout.
command: string[];
timeoutSeconds?: number;
2026-01-04 07:05:04 +01:00
};
};
export type MessagesConfig = {
/** @deprecated Use `whatsapp.messagePrefix` (WhatsApp-only inbound prefix). */
messagePrefix?: string;
/**
* Prefix auto-added to all outbound replies.
* - string: explicit prefix
* - special value: `"auto"` derives `[{agents.list[].identity.name}]` for the routed agent (when set)
* Default: none
*/
responsePrefix?: string;
groupChat?: GroupChatConfig;
queue?: QueueConfig;
2026-01-06 03:28:35 +00:00
/** 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;
2026-01-04 07:05:04 +01:00
};
2026-01-12 21:49:44 +00:00
export type NativeCommandsSetting = boolean | "auto";
export type CommandsConfig = {
2026-01-12 21:49:44 +00:00
/** Enable native command registration when supported (default: "auto"). */
native?: 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;
2026-01-09 05:49:11 +00:00
/** Allow restart commands/tools (default: false). */
restart?: boolean;
/** Enforce access-group allowlists/policies for commands (default: true). */
useAccessGroups?: boolean;
};
2026-01-12 21:49:44 +00:00
export type ProviderCommandsConfig = {
/** Override native command registration for this provider (bool or "auto"). */
native?: NativeCommandsSetting;
};
feat: add Tailscale binary detection, IP binding modes, and health probe password fix This PR includes three main improvements: 1. Tailscale Binary Detection with Fallback Strategies - Added findTailscaleBinary() with multi-strategy detection: * PATH lookup via 'which' command * Known macOS app path (/Applications/Tailscale.app/Contents/MacOS/Tailscale) * find /Applications for Tailscale.app * locate database lookup - Added getTailscaleBinary() with caching - Updated all Tailscale operations to use detected binary - Added TUI warning when Tailscale binary not found for serve/funnel modes 2. Custom Gateway IP Binding with Fallback - New bind mode "custom" allowing user-specified IP with fallback to 0.0.0.0 - Removed "tailnet" mode (folded into "auto") - All modes now support graceful fallback: custom (if fail → 0.0.0.0), loopback (127.0.0.1 → 0.0.0.0), auto (tailnet → 0.0.0.0), lan (0.0.0.0) - Added customBindHost config option for custom bind mode - Added canBindTo() helper to test IP availability before binding - Updated configure and onboarding wizards with new bind mode options 3. Health Probe Password Auth Fix - Gateway probe now tries both new and old passwords - Fixes issue where password change fails health check if gateway hasn't restarted yet - Uses nextConfig password first, falls back to baseConfig password if needed Files changed: - src/infra/tailscale.ts: Binary detection + caching - src/gateway/net.ts: IP binding with fallback logic - src/config/types.ts: BridgeBindMode type + customBindHost field - src/commands/configure.ts: Health probe dual-password try + Tailscale detection warning + bind mode UI - src/wizard/onboarding.ts: Tailscale detection warning + bind mode UI - src/gateway/server.ts: Use new resolveGatewayBindHost - src/gateway/call.ts: Updated preferTailnet logic (removed "tailnet" mode) - src/commands/onboard-types.ts: Updated GatewayBind type - src/commands/onboard-helpers.ts: resolveControlUiLinks updated - src/cli/*.ts: Updated bind mode casts - src/gateway/call.test.ts: Removed "tailnet" mode test
2026-01-11 14:13:13 -06:00
export type BridgeBindMode = "auto" | "lan" | "loopback" | "custom";
2026-01-04 07:05:04 +01:00
export type BridgeConfig = {
enabled?: boolean;
port?: number;
/**
* Bind address policy for the node bridge server.
feat: add Tailscale binary detection, IP binding modes, and health probe password fix This PR includes three main improvements: 1. Tailscale Binary Detection with Fallback Strategies - Added findTailscaleBinary() with multi-strategy detection: * PATH lookup via 'which' command * Known macOS app path (/Applications/Tailscale.app/Contents/MacOS/Tailscale) * find /Applications for Tailscale.app * locate database lookup - Added getTailscaleBinary() with caching - Updated all Tailscale operations to use detected binary - Added TUI warning when Tailscale binary not found for serve/funnel modes 2. Custom Gateway IP Binding with Fallback - New bind mode "custom" allowing user-specified IP with fallback to 0.0.0.0 - Removed "tailnet" mode (folded into "auto") - All modes now support graceful fallback: custom (if fail → 0.0.0.0), loopback (127.0.0.1 → 0.0.0.0), auto (tailnet → 0.0.0.0), lan (0.0.0.0) - Added customBindHost config option for custom bind mode - Added canBindTo() helper to test IP availability before binding - Updated configure and onboarding wizards with new bind mode options 3. Health Probe Password Auth Fix - Gateway probe now tries both new and old passwords - Fixes issue where password change fails health check if gateway hasn't restarted yet - Uses nextConfig password first, falls back to baseConfig password if needed Files changed: - src/infra/tailscale.ts: Binary detection + caching - src/gateway/net.ts: IP binding with fallback logic - src/config/types.ts: BridgeBindMode type + customBindHost field - src/commands/configure.ts: Health probe dual-password try + Tailscale detection warning + bind mode UI - src/wizard/onboarding.ts: Tailscale detection warning + bind mode UI - src/gateway/server.ts: Use new resolveGatewayBindHost - src/gateway/call.ts: Updated preferTailnet logic (removed "tailnet" mode) - src/commands/onboard-types.ts: Updated GatewayBind type - src/commands/onboard-helpers.ts: resolveControlUiLinks updated - src/cli/*.ts: Updated bind mode casts - src/gateway/call.test.ts: Removed "tailnet" mode test
2026-01-11 14:13:13 -06:00
* - auto: Tailnet IPv4 if available, else 0.0.0.0 (fallback to all interfaces)
* - lan: 0.0.0.0 (all interfaces, no fallback)
* - loopback: 127.0.0.1 (local-only)
* - custom: User-specified IP, fallback to 0.0.0.0 if unavailable (requires customBindHost on gateway)
2026-01-04 07:05:04 +01:00
*/
bind?: BridgeBindMode;
};
export type WideAreaDiscoveryConfig = {
enabled?: boolean;
};
export type DiscoveryConfig = {
wideArea?: WideAreaDiscoveryConfig;
};
export type CanvasHostConfig = {
enabled?: boolean;
/** Directory to serve (default: ~/clawd/canvas). */
root?: string;
/** HTTP port to listen on (default: 18793). */
port?: number;
2026-01-04 15:22:47 +01:00
/** Enable live-reload file watching + WS reloads (default: true). */
liveReload?: boolean;
2026-01-04 07:05:04 +01:00
};
export type TalkConfig = {
/** Default ElevenLabs voice ID for Talk mode. */
voiceId?: string;
/** Optional voice name -> ElevenLabs voice ID map. */
voiceAliases?: Record<string, string>;
/** Default ElevenLabs model ID for Talk mode. */
modelId?: string;
/** Default ElevenLabs output format (e.g. mp3_44100_128). */
outputFormat?: string;
/** ElevenLabs API key (optional; falls back to ELEVENLABS_API_KEY). */
apiKey?: string;
/** Stop speaking when user starts talking (default: true). */
interruptOnSpeech?: boolean;
};
export type GatewayControlUiConfig = {
/** If false, the Gateway will not serve the Control UI (default /). */
enabled?: boolean;
2026-01-04 14:32:47 +00:00
/** Optional base path prefix for the Control UI (e.g. "/clawdbot"). */
2026-01-04 07:05:04 +01:00
basePath?: string;
};
export type GatewayAuthMode = "token" | "password";
export type GatewayAuthConfig = {
/** Authentication mode for Gateway connections. Defaults to token when set. */
mode?: GatewayAuthMode;
/** Shared token for token mode (stored locally for CLI auth). */
token?: string;
/** Shared password for password mode (consider env instead). */
password?: string;
/** Allow Tailscale identity headers when serve mode is enabled. */
allowTailscale?: boolean;
};
export type GatewayTailscaleMode = "off" | "serve" | "funnel";
export type GatewayTailscaleConfig = {
/** Tailscale exposure mode for the Gateway control UI. */
mode?: GatewayTailscaleMode;
/** Reset serve/funnel configuration on shutdown. */
resetOnExit?: boolean;
};
export type GatewayRemoteConfig = {
/** Remote Gateway WebSocket URL (ws:// or wss://). */
url?: string;
/** Token for remote auth (when the gateway requires token auth). */
token?: string;
/** Password for remote auth (when the gateway requires password auth). */
password?: string;
/** SSH target for tunneling remote Gateway (user@host). */
sshTarget?: string;
/** SSH identity file path for tunneling remote Gateway. */
sshIdentity?: string;
2026-01-04 07:05:04 +01:00
};
export type GatewayReloadMode = "off" | "restart" | "hot" | "hybrid";
export type GatewayReloadConfig = {
/** Reload strategy for config changes (default: hybrid). */
mode?: GatewayReloadMode;
/** Debounce window for config reloads (ms). Default: 300. */
debounceMs?: number;
};
export type GatewayHttpChatCompletionsConfig = {
/**
* If false, the Gateway will not serve `POST /v1/chat/completions`.
* Default: false when absent.
*/
enabled?: boolean;
};
export type GatewayHttpEndpointsConfig = {
chatCompletions?: GatewayHttpChatCompletionsConfig;
};
export type GatewayHttpConfig = {
endpoints?: GatewayHttpEndpointsConfig;
};
2026-01-04 07:05:04 +01:00
export type GatewayConfig = {
/** Single multiplexed port for Gateway WS + HTTP (default: 18789). */
port?: number;
/**
* Explicit gateway mode. When set to "remote", local gateway start is disabled.
* When set to "local", the CLI may start the gateway locally.
*/
mode?: "local" | "remote";
/**
* Bind address policy for the Gateway WebSocket + Control UI HTTP server.
feat: add Tailscale binary detection, IP binding modes, and health probe password fix This PR includes three main improvements: 1. Tailscale Binary Detection with Fallback Strategies - Added findTailscaleBinary() with multi-strategy detection: * PATH lookup via 'which' command * Known macOS app path (/Applications/Tailscale.app/Contents/MacOS/Tailscale) * find /Applications for Tailscale.app * locate database lookup - Added getTailscaleBinary() with caching - Updated all Tailscale operations to use detected binary - Added TUI warning when Tailscale binary not found for serve/funnel modes 2. Custom Gateway IP Binding with Fallback - New bind mode "custom" allowing user-specified IP with fallback to 0.0.0.0 - Removed "tailnet" mode (folded into "auto") - All modes now support graceful fallback: custom (if fail → 0.0.0.0), loopback (127.0.0.1 → 0.0.0.0), auto (tailnet → 0.0.0.0), lan (0.0.0.0) - Added customBindHost config option for custom bind mode - Added canBindTo() helper to test IP availability before binding - Updated configure and onboarding wizards with new bind mode options 3. Health Probe Password Auth Fix - Gateway probe now tries both new and old passwords - Fixes issue where password change fails health check if gateway hasn't restarted yet - Uses nextConfig password first, falls back to baseConfig password if needed Files changed: - src/infra/tailscale.ts: Binary detection + caching - src/gateway/net.ts: IP binding with fallback logic - src/config/types.ts: BridgeBindMode type + customBindHost field - src/commands/configure.ts: Health probe dual-password try + Tailscale detection warning + bind mode UI - src/wizard/onboarding.ts: Tailscale detection warning + bind mode UI - src/gateway/server.ts: Use new resolveGatewayBindHost - src/gateway/call.ts: Updated preferTailnet logic (removed "tailnet" mode) - src/commands/onboard-types.ts: Updated GatewayBind type - src/commands/onboard-helpers.ts: resolveControlUiLinks updated - src/cli/*.ts: Updated bind mode casts - src/gateway/call.test.ts: Removed "tailnet" mode test
2026-01-11 14:13:13 -06:00
* - auto: Tailnet IPv4 if available, else 0.0.0.0 (fallback to all interfaces)
* - lan: 0.0.0.0 (all interfaces, no fallback)
* - loopback: 127.0.0.1 (local-only)
* - custom: User-specified IP, fallback to 0.0.0.0 if unavailable (requires customBindHost)
2026-01-04 07:05:04 +01:00
* Default: loopback (127.0.0.1).
*/
bind?: BridgeBindMode;
feat: add Tailscale binary detection, IP binding modes, and health probe password fix This PR includes three main improvements: 1. Tailscale Binary Detection with Fallback Strategies - Added findTailscaleBinary() with multi-strategy detection: * PATH lookup via 'which' command * Known macOS app path (/Applications/Tailscale.app/Contents/MacOS/Tailscale) * find /Applications for Tailscale.app * locate database lookup - Added getTailscaleBinary() with caching - Updated all Tailscale operations to use detected binary - Added TUI warning when Tailscale binary not found for serve/funnel modes 2. Custom Gateway IP Binding with Fallback - New bind mode "custom" allowing user-specified IP with fallback to 0.0.0.0 - Removed "tailnet" mode (folded into "auto") - All modes now support graceful fallback: custom (if fail → 0.0.0.0), loopback (127.0.0.1 → 0.0.0.0), auto (tailnet → 0.0.0.0), lan (0.0.0.0) - Added customBindHost config option for custom bind mode - Added canBindTo() helper to test IP availability before binding - Updated configure and onboarding wizards with new bind mode options 3. Health Probe Password Auth Fix - Gateway probe now tries both new and old passwords - Fixes issue where password change fails health check if gateway hasn't restarted yet - Uses nextConfig password first, falls back to baseConfig password if needed Files changed: - src/infra/tailscale.ts: Binary detection + caching - src/gateway/net.ts: IP binding with fallback logic - src/config/types.ts: BridgeBindMode type + customBindHost field - src/commands/configure.ts: Health probe dual-password try + Tailscale detection warning + bind mode UI - src/wizard/onboarding.ts: Tailscale detection warning + bind mode UI - src/gateway/server.ts: Use new resolveGatewayBindHost - src/gateway/call.ts: Updated preferTailnet logic (removed "tailnet" mode) - src/commands/onboard-types.ts: Updated GatewayBind type - src/commands/onboard-helpers.ts: resolveControlUiLinks updated - src/cli/*.ts: Updated bind mode casts - src/gateway/call.test.ts: Removed "tailnet" mode test
2026-01-11 14:13:13 -06:00
/** Custom IP address for bind="custom" mode. Fallback: 0.0.0.0. */
customBindHost?: string;
2026-01-04 07:05:04 +01:00
controlUi?: GatewayControlUiConfig;
auth?: GatewayAuthConfig;
tailscale?: GatewayTailscaleConfig;
remote?: GatewayRemoteConfig;
reload?: GatewayReloadConfig;
http?: GatewayHttpConfig;
2026-01-04 07:05:04 +01:00
};
export type SkillConfig = {
enabled?: boolean;
apiKey?: string;
env?: Record<string, string>;
[key: string]: unknown;
};
export type SkillsLoadConfig = {
/**
* Additional skill folders to scan (lowest precedence).
* Each directory should contain skill subfolders with `SKILL.md`.
*/
extraDirs?: string[];
};
export type SkillsInstallConfig = {
preferBrew?: boolean;
nodeManager?: "npm" | "pnpm" | "yarn" | "bun";
};
export type SkillsConfig = {
/** Optional bundled-skill allowlist (only affects bundled skills). */
allowBundled?: string[];
load?: SkillsLoadConfig;
install?: SkillsInstallConfig;
entries?: Record<string, SkillConfig>;
};
2026-01-11 12:11:12 +00:00
export type PluginEntryConfig = {
enabled?: boolean;
config?: Record<string, unknown>;
};
export type PluginsLoadConfig = {
/** Additional plugin/extension paths to load. */
paths?: string[];
};
export type PluginsConfig = {
/** Enable or disable plugin loading. */
enabled?: boolean;
/** Optional plugin allowlist (plugin ids). */
allow?: string[];
/** Optional plugin denylist (plugin ids). */
deny?: string[];
load?: PluginsLoadConfig;
entries?: Record<string, PluginEntryConfig>;
};
2026-01-04 07:05:04 +01:00
export type ModelApi =
| "openai-completions"
| "openai-responses"
| "anthropic-messages"
| "google-generative-ai"
| "github-copilot";
2026-01-04 07:05:04 +01:00
export type ModelCompatConfig = {
supportsStore?: boolean;
supportsDeveloperRole?: boolean;
supportsReasoningEffort?: boolean;
maxTokensField?: "max_completion_tokens" | "max_tokens";
};
export type ModelDefinitionConfig = {
id: string;
name: string;
api?: ModelApi;
reasoning: boolean;
input: Array<"text" | "image">;
cost: {
input: number;
output: number;
cacheRead: number;
cacheWrite: number;
};
contextWindow: number;
maxTokens: number;
headers?: Record<string, string>;
compat?: ModelCompatConfig;
};
export type ModelProviderConfig = {
baseUrl: string;
apiKey?: string;
2026-01-04 07:05:04 +01:00
api?: ModelApi;
headers?: Record<string, string>;
authHeader?: boolean;
models: ModelDefinitionConfig[];
};
export type ModelsConfig = {
mode?: "merge" | "replace";
providers?: Record<string, ModelProviderConfig>;
};
export type AuthProfileConfig = {
provider: string;
2026-01-09 07:51:47 +01:00
/**
* Credential type expected in auth-profiles.json for this profile id.
* - api_key: static provider API key
* - oauth: refreshable OAuth credentials (access+refresh+expires)
* - token: static bearer-style token (optionally expiring; no refresh)
*/
mode: "api_key" | "oauth" | "token";
email?: string;
};
export type AuthConfig = {
profiles?: Record<string, AuthProfileConfig>;
order?: Record<string, string[]>;
cooldowns?: {
/** Default billing backoff (hours). Default: 5. */
billingBackoffHours?: number;
/** Optional per-provider billing backoff (hours). */
billingBackoffHoursByProvider?: Record<string, number>;
/** Billing backoff cap (hours). Default: 24. */
billingMaxHours?: number;
/**
* Failure window for backoff counters (hours). If no failures occur within
* this window, counters reset. Default: 24.
*/
failureWindowHours?: number;
};
};
export type AgentModelEntryConfig = {
alias?: string;
/** Provider-specific API parameters (e.g., GLM-4.7 thinking mode). */
params?: Record<string, unknown>;
};
export type AgentModelListConfig = {
primary?: string;
fallbacks?: string[];
};
export type AgentContextPruningConfig = {
mode?: "off" | "adaptive" | "aggressive";
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;
};
};
2026-01-10 23:31:25 +00:00
export 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). */
2026-01-11 01:35:23 +00:00
output?: "json" | "text" | "jsonl";
/** Output parsing mode when resuming a CLI session. */
resumeOutput?: "json" | "text" | "jsonl";
2026-01-10 23:31:25 +00:00
/** 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;
2026-01-11 01:35:23 +00:00
/** 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[];
2026-01-10 23:31:25 +00:00
/** 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;
};
export 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;
/** 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;
/** Optional display-only context window override (used for % in status UIs). */
contextTokens?: number;
2026-01-10 23:31:25 +00:00
/** 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;
2026-01-12 05:28:17 +00:00
/** Compaction tuning and pre-compaction memory flush behavior. */
compaction?: AgentCompactionConfig;
2026-01-12 11:22:56 +00:00
/** Vector memory search configuration (per-agent overrides supported). */
memorySearch?: MemorySearchConfig;
/** Default thinking level when no /think directive is present. */
2026-01-07 17:17:38 -08:00
thinkingDefault?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
/** Default verbose level when no /verbose directive is present. */
verboseDefault?: "off" | "on";
/** Default elevated level when no /elevated directive is present. */
elevatedDefault?: "off" | "on";
/** 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;
/** Heartbeat model override (provider/model). */
model?: string;
/** Delivery target (last|whatsapp|telegram|discord|slack|msteams|signal|imessage|none). */
target?:
| "last"
| "whatsapp"
| "telegram"
| "discord"
| "slack"
| "msteams"
| "signal"
| "imessage"
| "none";
/** Optional delivery override (E.164 for WhatsApp, chat id for Telegram). */
to?: string;
/** Override the heartbeat prompt body (default: "Read HEARTBEAT.md if exists. Consider outstanding tasks. Checkup sometimes on your human during (user local) day time."). */
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;
2026-01-12 18:18:03 +00:00
/** Default model selection for spawned sub-agents (string or {primary,fallbacks}). */
model?: string | { primary?: string; fallbacks?: 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;
};
};
export type AgentCompactionMode = "default" | "safeguard";
2026-01-12 05:28:17 +00:00
export type AgentCompactionConfig = {
/** Compaction summarization mode. */
mode?: AgentCompactionMode;
2026-01-12 05:28:17 +00:00
/** Minimum reserve tokens enforced for Pi compaction (0 disables the floor). */
reserveTokensFloor?: number;
/** Pre-compaction memory flush (agentic turn). Default: enabled. */
memoryFlush?: AgentCompactionMemoryFlushConfig;
};
export 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;
};
2026-01-04 14:32:47 +00:00
export type ClawdbotConfig = {
auth?: AuthConfig;
2026-01-05 00:59:25 +01:00
env?: {
/** Opt-in: import missing secrets from a login shell environment (exec `$SHELL -l -c 'env -0'`). */
shellEnv?: {
enabled?: boolean;
/** Timeout for the login shell exec (ms). Default: 15000. */
timeoutMs?: number;
};
2026-01-08 22:37:06 +01:00
/** Inline env vars to apply when not already present in the process env. */
vars?: Record<string, string>;
/** Sugar: allow env vars directly under env (string values only). */
[key: string]:
| string
| Record<string, string>
| { enabled?: boolean; timeoutMs?: number }
| undefined;
2026-01-05 00:59:25 +01:00
};
2026-01-04 07:05:04 +01:00
wizard?: {
lastRunAt?: string;
lastRunVersion?: string;
lastRunCommit?: string;
lastRunCommand?: string;
lastRunMode?: "local" | "remote";
};
logging?: LoggingConfig;
browser?: BrowserConfig;
ui?: {
2026-01-04 14:32:47 +00:00
/** Accent color for Clawdbot UI chrome (hex). */
2026-01-04 07:05:04 +01:00
seamColor?: string;
};
skills?: SkillsConfig;
2026-01-11 12:11:12 +00:00
plugins?: PluginsConfig;
2026-01-04 07:05:04 +01:00
models?: ModelsConfig;
agents?: AgentsConfig;
tools?: ToolsConfig;
bindings?: AgentBinding[];
broadcast?: BroadcastConfig;
audio?: AudioConfig;
2026-01-04 07:05:04 +01:00
messages?: MessagesConfig;
commands?: CommandsConfig;
2026-01-04 07:05:04 +01:00
session?: SessionConfig;
web?: WebConfig;
channels?: ChannelsConfig;
2026-01-04 07:05:04 +01:00
cron?: CronConfig;
hooks?: HooksConfig;
bridge?: BridgeConfig;
discovery?: DiscoveryConfig;
canvasHost?: CanvasHostConfig;
talk?: TalkConfig;
gateway?: GatewayConfig;
};
export type ConfigValidationIssue = {
path: string;
message: string;
};
export type LegacyConfigIssue = {
path: string;
message: string;
};
export type ConfigFileSnapshot = {
path: string;
exists: boolean;
raw: string | null;
parsed: unknown;
valid: boolean;
2026-01-04 14:32:47 +00:00
config: ClawdbotConfig;
2026-01-04 07:05:04 +01:00
issues: ConfigValidationIssue[];
legacyIssues: LegacyConfigIssue[];
};