配置 🔧
OpenClaw 会从 ~/.openclaw/openclaw.json 读取一个可选的 JSON5 配置(允许注释与尾随逗号)。
如果该文件不存在,OpenClaw 会使用相对安全的默认值(内置 Pi agent + 按发送者分会话 + 工作区 ~/.openclaw/workspace)。一般你只在以下场景需要写配置:
- 限制谁可以触发 bot(
channels.whatsapp.allowFrom、channels.telegram.allowFrom等) - 控制群聊 allowlist 与提及触发行为(
channels.whatsapp.groups、channels.telegram.groups、channels.discord.guilds、agents.list[].groupChat) - 自定义消息前缀(
messages) - 设置 agent 的工作区(
agents.defaults.workspace或agents.list[].workspace) - 调整内置 agent 默认值(
agents.defaults)与会话行为(session) - 设置每个 agent 的 identity(
agents.list[].identity)
第一次写配置? 建议先看 Configuration Examples,里面有完整示例与详细讲解。
严格配置校验
OpenClaw 只接受 完全匹配 schema 的配置。 未知 key、类型错误或无效值都会导致 Gateway 为安全起见 拒绝启动。
当校验失败时:
- Gateway 不会启动。
- 只允许执行诊断相关命令(例如:
openclaw doctor、openclaw logs、openclaw health、openclaw status、openclaw service、openclaw help)。 - 运行
openclaw doctor查看具体问题。 - 运行
openclaw doctor --fix(或--yes)应用迁移/修复。
除非你明确选择 --fix/--yes,否则 doctor 不会写入任何修改。
Schema + UI hints
Gateway 会通过 config.schema 暴露配置的 JSON Schema 表示,用于 UI 编辑器。 Control UI 会基于该 schema 渲染表单,并提供 Raw JSON 编辑器作为“逃生舱”。
通道插件与扩展可以为它们的配置注册 schema + UI hints,让各个 app 的通道设置保持 schema 驱动,而不是写死表单。
Hints(label、分组、敏感字段等)会与 schema 一起下发,使客户端无需硬编码配置知识也能渲染更好的表单。
应用 + 重启(RPC)
使用 config.apply 可以在一步里完成:校验 + 写入完整配置 + 重启 Gateway。 它会写入一个 restart sentinel,并在 Gateway 恢复后 ping 最近一次活跃会话。
警告:config.apply 会替换 整个配置。如果你只想改少数 key,使用 config.patch 或 openclaw config set。建议保留 ~/.openclaw/openclaw.json 的备份。
参数:
raw(string)— 整份配置的 JSON5 payloadbaseHash(可选)— 来自config.get的 config hash(当配置已存在时必需)sessionKey(可选)— 用于 wake-up ping 的最近活跃 session keynote(可选)— 写入 restart sentinel 的备注restartDelayMs(可选)— 重启前延迟(默认 2000)
示例(通过 gateway call):
openclaw gateway call config.get --params '{}' # capture payload.hash
openclaw gateway call config.apply --params '{
"raw": "{\\n agents: { defaults: { workspace: \\"~/.openclaw/workspace\\" } }\\n}\\n",
"baseHash": "<hash-from-config.get>",
"sessionKey": "agent:main:whatsapp:dm:+15555550123",
"restartDelayMs": 1000
}'部分更新(RPC)
使用 config.patch 可以把部分更新合并到现有配置中,而不会覆盖无关的 key。它使用 JSON merge patch 语义:
- 对象递归合并
null删除一个 key- 数组直接替换 与
config.apply一样,它会校验并写入配置、存储 restart sentinel,并计划一次 Gateway 重启(当提供sessionKey时可选唤醒)。
参数:
raw(string)— 仅包含要修改 key 的 JSON5 payloadbaseHash(必需)— 来自config.get的 config hashsessionKey(可选)— 用于 wake-up ping 的最近活跃 session keynote(可选)— 写入 restart sentinel 的备注restartDelayMs(可选)— 重启前延迟(默认 2000)
示例:
openclaw gateway call config.get --params '{}' # capture payload.hash
openclaw gateway call config.patch --params '{
"raw": "{\\n channels: { telegram: { groups: { \\"*\\": { requireMention: false } } } }\\n}\\n",
"baseHash": "<hash-from-config.get>",
"sessionKey": "agent:main:whatsapp:dm:+15555550123",
"restartDelayMs": 1000
}'最小配置(推荐起点)
{
agents: { defaults: { workspace: "~/.openclaw/workspace" } },
channels: { whatsapp: { allowFrom: ["+15555550123"] } },
}构建一次默认镜像:
scripts/sandbox-setup.sh自聊模式(推荐用于群聊控制)
为防止 bot 在群聊里对 WhatsApp 的 @ 提及自动回复(只对特定文本触发做出响应):
{
agents: {
defaults: { workspace: "~/.openclaw/workspace" },
list: [
{
id: "main",
groupChat: { mentionPatterns: ["@openclaw", "reisponde"] },
},
],
},
channels: {
whatsapp: {
// Allowlist is DMs only; including your own number enables self-chat mode.
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } },
},
},
}配置包含($include)
使用 $include 指令把配置拆分到多个文件中。这在以下场景很有用:
- 组织大型配置(例如:按客户拆分的 agent 定义)
- 在不同环境之间共享通用设置
- 将敏感配置单独放置
Basic usage
// ~/.openclaw/openclaw.json
{
gateway: { port: 18789 },
// Include a single file (replaces the key's value)
agents: { $include: "./agents.json5" },
// Include multiple files (deep-merged in order)
broadcast: {
$include: ["./clients/mueller.json5", "./clients/schmidt.json5"],
},
}// ~/.openclaw/agents.json5
{
defaults: { sandbox: { mode: "all", scope: "session" } },
list: [{ id: "main", workspace: "~/.openclaw/workspace" }],
}合并行为
- 单文件:替换包含
$include的对象 - 文件数组:按顺序深度合并(后面的文件覆盖前面的文件)
- 带同级 key:在包含合并完成后再合并同级 key(覆盖包含进来的值)
- 同级 key + 数组/原始值:不支持(被包含的内容必须是对象)
// Sibling keys override included values
{
$include: "./base.json5", // { a: 1, b: 2 }
b: 99, // Result: { a: 1, b: 99 }
}Nested includes
被包含的文件也可以继续包含 $include 指令(最多 10 层深度):
// clients/mueller.json5
{
agents: { $include: "./mueller/agents.json5" },
broadcast: { $include: "./mueller/broadcast.json5" },
}Path resolution
- 相对路径:相对于包含它的文件解析
- Absolute paths: Used as-is
- 父目录:
../引用按预期工作
{ "$include": "./sub/config.json5" } // relative
{ "$include": "/etc/openclaw/base.json5" } // absolute
{ "$include": "../shared/common.json5" } // parent dir错误处理
- Missing file: Clear error with resolved path
- 解析错误:提示是哪个被包含文件解析失败
- Circular includes: Detected and reported with include chain
示例:多客户(合规隔离)配置
// ~/.openclaw/openclaw.json
{
gateway: { port: 18789, auth: { token: "secret" } },
// Common agent defaults
agents: {
defaults: {
sandbox: { mode: "all", scope: "session" },
},
// Merge agent lists from all clients
list: { $include: ["./clients/mueller/agents.json5", "./clients/schmidt/agents.json5"] },
},
// Merge broadcast configs
broadcast: {
$include: ["./clients/mueller/broadcast.json5", "./clients/schmidt/broadcast.json5"],
},
channels: { whatsapp: { groupPolicy: "allowlist" } },
}// ~/.openclaw/clients/mueller/agents.json5
[
{ id: "mueller-transcribe", workspace: "~/clients/mueller/transcribe" },
{ id: "mueller-docs", workspace: "~/clients/mueller/docs" },
]// ~/.openclaw/clients/mueller/broadcast.json5
{
"120363403215116621@g.us": ["mueller-transcribe", "mueller-docs"],
}常用选项
环境变量 + .env
OpenClaw 会从父进程读取环境变量(shell、launchd/systemd、CI 等)。
此外,它还会加载:
- 当前工作目录下的
.env(若存在) ~/.openclaw/.env的全局兜底.env(即$OPENCLAW_STATE_DIR/.env)
这两个 .env 文件都不会覆盖已存在的环境变量。
你也可以在配置里内联声明环境变量。它们只会在进程环境变量缺少该 key 时生效(同样遵循“不覆盖”规则):
{
env: {
OPENROUTER_API_KEY: "sk-or-...",
vars: {
GROQ_API_KEY: "gsk-...",
},
},
}完整的优先级与来源请见 /environment。
env.shellEnv(可选)
一个可选的便捷功能:启用后,当预期的 key 都还未设置时,OpenClaw 会运行你的登录 shell,并只导入缺失的预期 key(从不覆盖已存在值)。 这相当于“source”了你的 shell profile。
{
env: {
shellEnv: {
enabled: true,
timeoutMs: 15000,
},
},
}对应的环境变量:
OPENCLAW_LOAD_SHELL_ENV=1OPENCLAW_SHELL_ENV_TIMEOUT_MS=15000
配置中的环境变量替换
你可以在任意配置字符串值中直接使用 ${VAR_NAME} 语法引用环境变量。 变量会在配置加载时、校验之前进行替换。
{
models: {
providers: {
"vercel-gateway": {
apiKey: "${VERCEL_GATEWAY_API_KEY}",
},
},
},
gateway: {
auth: {
token: "${OPENCLAW_GATEWAY_TOKEN}",
},
},
}规则:
- 只匹配全大写的环境变量名:
[A-Z_][A-Z0-9_]* - 缺失或为空的环境变量会在配置加载时抛错
- 用
$${VAR}进行转义以输出字面量${VAR} - 可与
$include一起使用(被包含文件也会进行替换)
内联替换:
{
models: {
providers: {
custom: {
baseUrl: "${CUSTOM_API_BASE}/v1", // → "https://api.example.com/v1"
},
},
},
}Auth storage(OAuth + API keys,鉴权存储)
OpenClaw 会把按 agent 隔离的 auth profiles(OAuth + API keys)存放在:
<agentDir>/auth-profiles.json(默认:~/.openclaw/agents/<agentId>/agent/auth-profiles.json)
旧版 OAuth 导入:
~/.openclaw/credentials/oauth.json(或$OPENCLAW_STATE_DIR/credentials/oauth.json)
内嵌的 Pi agent 会维护一个运行时缓存:
<agentDir>/auth.json(自动管理;请勿手工编辑)
旧版 agent 目录(multi-agent 之前):
~/.openclaw/agent/*(会被openclaw doctor迁移到~/.openclaw/agents/<defaultAgentId>/agent/*)
覆盖项(Overrides):
- OAuth dir(仅用于旧版导入):
OPENCLAW_OAUTH_DIR - Agent dir(默认 agent root 覆盖):
OPENCLAW_AGENT_DIR(推荐)、PI_CODING_AGENT_DIR(legacy)
首次使用时,OpenClaw 会把 oauth.json 的条目导入到 auth-profiles.json。
auth
auth profile 的可选元数据。它不存储 secrets;它把 profile IDs 映射到 provider + mode(以及可选 email),并定义用于 failover 的 provider 轮换顺序。
{
auth: {
profiles: {
"anthropic:me@example.com": { provider: "anthropic", mode: "oauth", email: "me@example.com" },
"anthropic:work": { provider: "anthropic", mode: "api_key" },
},
order: {
anthropic: ["anthropic:me@example.com", "anthropic:work"],
},
},
}agents.list[].identity
Optional per-agent identity used for defaults and UX. This is written by the macOS onboarding assistant.
If set, OpenClaw derives defaults (only when you haven’t set them explicitly):
messages.ackReactionfrom the active agent’sidentity.emoji(falls back to 👀)agents.list[].groupChat.mentionPatternsfrom the agent’sidentity.name/identity.emoji(so “@Samantha” works in groups across Telegram/Slack/Discord/Google Chat/iMessage/WhatsApp)identity.avataraccepts a workspace-relative image path or a remote URL/data URL. Local files must live inside the agent workspace.
identity.avatar accepts:
- Workspace-relative path (must stay within the agent workspace)
http(s)URLdata:URI
{
agents: {
list: [
{
id: "main",
identity: {
name: "Samantha",
theme: "helpful sloth",
emoji: "🦥",
avatar: "avatars/samantha.png",
},
},
],
},
}wizard
Metadata written by CLI wizards (onboard, configure, doctor).
{
wizard: {
lastRunAt: "2026-01-01T00:00:00.000Z",
lastRunVersion: "2026.1.4",
lastRunCommit: "abc1234",
lastRunCommand: "configure",
lastRunMode: "local",
},
}logging
- Default log file:
/tmp/openclaw/openclaw-YYYY-MM-DD.log - If you want a stable path, set
logging.fileto/tmp/openclaw/openclaw.log. - Console output can be tuned separately via:
logging.consoleLevel(defaults toinfo, bumps todebugwhen--verbose)logging.consoleStyle(pretty|compact|json)
- Tool summaries can be redacted to avoid leaking secrets:
logging.redactSensitive(off|tools, default:tools)logging.redactPatterns(array of regex strings; overrides defaults)
{
logging: {
level: "info",
file: "/tmp/openclaw/openclaw.log",
consoleLevel: "info",
consoleStyle: "pretty",
redactSensitive: "tools",
redactPatterns: [
// Example: override defaults with your own rules.
"\\bTOKEN\\b\\s*[=:]\\s*([\"']?)([^\\s\"']+)\\1",
"/\\bsk-[A-Za-z0-9_-]{8,}\\b/gi",
],
},
}channels.whatsapp.dmPolicy
Controls how WhatsApp direct chats (DMs) are handled:
"pairing"(default): unknown senders get a pairing code; owner must approve"allowlist": only allow senders inchannels.whatsapp.allowFrom(or paired allow store)"open": allow all inbound DMs (requireschannels.whatsapp.allowFromto include"*")"disabled": ignore all inbound DMs
Pairing codes expire after 1 hour; the bot only sends a pairing code when a new request is created. Pending DM pairing requests are capped at 3 per channel by default.
Pairing approvals:
openclaw pairing list whatsappopenclaw pairing approve whatsapp <code>
channels.whatsapp.allowFrom
Allowlist of E.164 phone numbers that may trigger WhatsApp auto-replies (DMs only). If empty and channels.whatsapp.dmPolicy="pairing", unknown senders will receive a pairing code. For groups, use channels.whatsapp.groupPolicy + channels.whatsapp.groupAllowFrom.
{
channels: {
whatsapp: {
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["+15555550123", "+447700900123"],
textChunkLimit: 4000, // optional outbound chunk size (chars)
chunkMode: "length", // optional chunking mode (length | newline)
mediaMaxMb: 50, // optional inbound media cap (MB)
},
},
}channels.whatsapp.sendReadReceipts
Controls whether inbound WhatsApp messages are marked as read (blue ticks). Default: true.
Self-chat mode always skips read receipts, even when enabled.
Per-account override: channels.whatsapp.accounts.<id>.sendReadReceipts.
{
channels: {
whatsapp: { sendReadReceipts: false },
},
}channels.whatsapp.accounts (multi-account)
Run multiple WhatsApp accounts in one gateway:
{
channels: {
whatsapp: {
accounts: {
default: {}, // optional; keeps the default id stable
personal: {},
biz: {
// Optional override. Default: ~/.openclaw/credentials/whatsapp/biz
// authDir: "~/.openclaw/credentials/whatsapp/biz",
},
},
},
},
}Notes:
- Outbound commands default to account
defaultif present; otherwise the first configured account id (sorted). - The legacy single-account Baileys auth dir is migrated by
openclaw doctorintowhatsapp/default.
channels.telegram.accounts / channels.discord.accounts / channels.googlechat.accounts / channels.slack.accounts / channels.mattermost.accounts / channels.signal.accounts / channels.imessage.accounts
Run multiple accounts per channel (each account has its own accountId and optional name):
{
channels: {
telegram: {
accounts: {
default: {
name: "Primary bot",
botToken: "123456:ABC...",
},
alerts: {
name: "Alerts bot",
botToken: "987654:XYZ...",
},
},
},
},
}Notes:
defaultis used whenaccountIdis omitted (CLI + routing).- Env tokens only apply to the default account.
- Base channel settings (group policy, mention gating, etc.) apply to all accounts unless overridden per account.
- Use
bindings[].match.accountIdto route each account to a different agents.defaults.
Group chat mention gating (agents.list[].groupChat + messages.groupChat)
Group messages default to require mention (either metadata mention or regex patterns). Applies to WhatsApp, Telegram, Discord, Google Chat, and iMessage group chats.
Mention types:
- Metadata mentions: Native platform @-mentions (e.g., WhatsApp tap-to-mention). Ignored in WhatsApp self-chat mode (see
channels.whatsapp.allowFrom). - Text patterns: Regex patterns defined in
agents.list[].groupChat.mentionPatterns. Always checked regardless of self-chat mode. - Mention gating is enforced only when mention detection is possible (native mentions or at least one
mentionPattern).
{
messages: {
groupChat: { historyLimit: 50 },
},
agents: {
list: [{ id: "main", groupChat: { mentionPatterns: ["@openclaw", "openclaw"] } }],
},
}messages.groupChat.historyLimit sets the global default for group history context. Channels can override with channels.<channel>.historyLimit (or channels.<channel>.accounts.*.historyLimit for multi-account). Set 0 to disable history wrapping.
DM history limits
DM conversations use session-based history managed by the agent. You can limit the number of user turns retained per DM session:
{
channels: {
telegram: {
dmHistoryLimit: 30, // limit DM sessions to 30 user turns
dms: {
"123456789": { historyLimit: 50 }, // per-user override (user ID)
},
},
},
}Resolution order:
- Per-DM override:
channels.<provider>.dms[userId].historyLimit - Provider default:
channels.<provider>.dmHistoryLimit - No limit (all history retained)
Supported providers: telegram, whatsapp, discord, slack, signal, imessage, msteams.
Per-agent override (takes precedence when set, even []):
{
agents: {
list: [
{ id: "work", groupChat: { mentionPatterns: ["@workbot", "\\+15555550123"] } },
{ id: "personal", groupChat: { mentionPatterns: ["@homebot", "\\+15555550999"] } },
],
},
}Mention gating defaults live per channel (channels.whatsapp.groups, channels.telegram.groups, channels.imessage.groups, channels.discord.guilds). When *.groups is set, it also acts as a group allowlist; include "*" to allow all groups.
To respond only to specific text triggers (ignoring native @-mentions):
{
channels: {
whatsapp: {
// Include your own number to enable self-chat mode (ignore native @-mentions).
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } },
},
},
agents: {
list: [
{
id: "main",
groupChat: {
// Only these text patterns will trigger responses
mentionPatterns: ["reisponde", "@openclaw"],
},
},
],
},
}Group policy (per channel)
Use channels.*.groupPolicy to control whether group/room messages are accepted at all:
{
channels: {
whatsapp: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"],
},
telegram: {
groupPolicy: "allowlist",
groupAllowFrom: ["tg:123456789", "@alice"],
},
signal: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"],
},
imessage: {
groupPolicy: "allowlist",
groupAllowFrom: ["chat_id:123"],
},
msteams: {
groupPolicy: "allowlist",
groupAllowFrom: ["user@org.com"],
},
discord: {
groupPolicy: "allowlist",
guilds: {
GUILD_ID: {
channels: { help: { allow: true } },
},
},
},
slack: {
groupPolicy: "allowlist",
channels: { "#general": { allow: true } },
},
},
}Notes:
"open": groups bypass allowlists; mention-gating still applies."disabled": block all group/room messages."allowlist": only allow groups/rooms that match the configured allowlist.channels.defaults.groupPolicysets the default when a provider’sgroupPolicyis unset.- WhatsApp/Telegram/Signal/iMessage/Microsoft Teams use
groupAllowFrom(fallback: explicitallowFrom). - Discord/Slack use channel allowlists (
channels.discord.guilds.*.channels,channels.slack.channels). - Group DMs (Discord/Slack) are still controlled by
dm.groupEnabled+dm.groupChannels. - Default is
groupPolicy: "allowlist"(unless overridden bychannels.defaults.groupPolicy); if no allowlist is configured, group messages are blocked.
Multi-agent routing (agents.list + bindings)
Run multiple isolated agents (separate workspace, agentDir, sessions) inside one Gateway. Inbound messages are routed to an agent via bindings.
agents.list[]: per-agent overrides.id: stable agent id (required).default: optional; when multiple are set, the first wins and a warning is logged. If none are set, the first entry in the list is the default agent.name: display name for the agent.workspace: default~/.openclaw/workspace-<agentId>(formain, falls back toagents.defaults.workspace).agentDir: default~/.openclaw/agents/<agentId>/agent.model: per-agent default model, overridesagents.defaults.modelfor that agent.- string form:
"provider/model", overrides onlyagents.defaults.model.primary - object form:
{ primary, fallbacks }(fallbacks overrideagents.defaults.model.fallbacks;[]disables global fallbacks for that agent)
- string form:
identity: per-agent name/theme/emoji (used for mention patterns + ack reactions).groupChat: per-agent mention-gating (mentionPatterns).sandbox: per-agent sandbox config (overridesagents.defaults.sandbox).mode:"off"|"non-main"|"all"workspaceAccess:"none"|"ro"|"rw"scope:"session"|"agent"|"shared"workspaceRoot: custom sandbox workspace rootdocker: per-agent docker overrides (e.g.image,network,env,setupCommand, limits; ignored whenscope: "shared")browser: per-agent sandboxed browser overrides (ignored whenscope: "shared")prune: per-agent sandbox pruning overrides (ignored whenscope: "shared")
subagents: per-agent sub-agent defaults.allowAgents: allowlist of agent ids forsessions_spawnfrom this agent (["*"]= allow any; default: only same agent)
tools: per-agent tool restrictions (applied before sandbox tool policy).profile: base tool profile (applied before allow/deny)allow: array of allowed tool namesdeny: array of denied tool names (deny wins)
agents.defaults: shared agent defaults (model, workspace, sandbox, etc.).bindings[]: routes inbound messages to anagentId.match.channel(required)match.accountId(optional;*= any account; omitted = default account)match.peer(optional;{ kind: dm|group|channel, id })match.guildId/match.teamId(optional; channel-specific)
Deterministic match order:
match.peermatch.guildIdmatch.teamIdmatch.accountId(exact, no peer/guild/team)match.accountId: "*"(channel-wide, no peer/guild/team)- default agent (
agents.list[].default, else first list entry, else"main")
Within each match tier, the first matching entry in bindings wins.
Per-agent access profiles (multi-agent)
Each agent can carry its own sandbox + tool policy. Use this to mix access levels in one gateway:
- Full access (personal agent)
- Read-only tools + workspace
- No filesystem access (messaging/session tools only)
See Multi-Agent Sandbox & Tools for precedence and additional examples.
Full access (no sandbox):
{
agents: {
list: [
{
id: "personal",
workspace: "~/.openclaw/workspace-personal",
sandbox: { mode: "off" },
},
],
},
}Read-only tools + read-only workspace:
{
agents: {
list: [
{
id: "family",
workspace: "~/.openclaw/workspace-family",
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "ro",
},
tools: {
allow: [
"read",
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"session_status",
],
deny: ["write", "edit", "apply_patch", "exec", "process", "browser"],
},
},
],
},
}No filesystem access (messaging/session tools enabled):
{
agents: {
list: [
{
id: "public",
workspace: "~/.openclaw/workspace-public",
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "none",
},
tools: {
allow: [
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"session_status",
"whatsapp",
"telegram",
"slack",
"discord",
"gateway",
],
deny: [
"read",
"write",
"edit",
"apply_patch",
"exec",
"process",
"browser",
"canvas",
"nodes",
"cron",
"gateway",
"image",
],
},
},
],
},
}Example: two WhatsApp accounts → two agents:
{
agents: {
list: [
{ id: "home", default: true, workspace: "~/.openclaw/workspace-home" },
{ id: "work", workspace: "~/.openclaw/workspace-work" },
],
},
bindings: [
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
],
channels: {
whatsapp: {
accounts: {
personal: {},
biz: {},
},
},
},
}tools.agentToAgent (optional)
Agent-to-agent messaging is opt-in:
{
tools: {
agentToAgent: {
enabled: false,
allow: ["home", "work"],
},
},
}messages.queue
Controls how inbound messages behave when an agent run is already active.
{
messages: {
queue: {
mode: "collect", // steer | followup | collect | steer-backlog (steer+backlog ok) | interrupt (queue=steer legacy)
debounceMs: 1000,
cap: 20,
drop: "summarize", // old | new | summarize
byChannel: {
whatsapp: "collect",
telegram: "collect",
discord: "collect",
imessage: "collect",
webchat: "collect",
},
},
},
}messages.inbound
对来自同一发送者的快速入站消息做去抖(debounce),这样多条连续消息会合并为一次 agent turn。 去抖的作用域是“每个 channel + 会话(conversation)”,并会使用最新一条消息来做回复串联/ID(threading/IDs)。
{
messages: {
inbound: {
debounceMs: 2000, // 0 disables
byChannel: {
whatsapp: 5000,
slack: 1500,
discord: 1500,
},
},
},
}备注:
- 去抖只会批量处理纯文本消息;媒体/附件会立即 flush(立刻触发一次 turn)。
- 控制命令(例如
/queue、/new)会绕过去抖,因此始终保持为独立消息。
commands (chat command handling)
控制在各个连接器(connectors)上如何启用聊天命令。
{
commands: {
native: "auto", // register native commands when supported (auto)
text: true, // parse slash commands in chat messages
bash: false, // allow ! (alias: /bash) (host-only; requires tools.elevated allowlists)
bashForegroundMs: 2000, // bash foreground window (0 backgrounds immediately)
config: false, // allow /config (writes to disk)
debug: false, // allow /debug (runtime-only overrides)
restart: false, // allow /restart + gateway restart tool
useAccessGroups: true, // enforce access-group allowlists/policies for commands
},
}备注:
- 文本命令必须以独立消息发送,并使用前导
/(不支持纯文本别名)。 commands.text: false会禁用在聊天消息中解析命令。commands.native: "auto"(默认)会为 Discord/Telegram 启用原生命令,并让 Slack 保持关闭;不支持原生命令的 channel 仍为纯文本命令。- 可将
commands.native: true|false强制全部开启/关闭;或使用channels.discord.commands.native、channels.telegram.commands.native、channels.slack.commands.native(bool 或"auto")做 per-channel 覆盖。对 Discord/Telegram,false会在启动时清理之前注册过的命令;Slack 命令由 Slack app 管理。 channels.telegram.customCommands会为 Telegram bot 菜单增加额外条目。名字会被规范化;与原生命令冲突的条目会被忽略。commands.bash: true会启用! <cmd>运行宿主机 shell 命令(/bash <cmd>也可作为别名)。需要tools.elevated.enabled,并在tools.elevated.allowFrom.<channel>中将发送者加入 allowlist。commands.bashForegroundMs控制 bash 在转入后台前会等待多久。bash 任务运行期间,新的! <cmd>请求会被拒绝(一次只能跑一个)。commands.config: true会启用/config(读取/写入openclaw.json)。channels.<provider>.configWrites用于限制该 channel 发起的 config 变更(默认:true)。这包括/config set|unset,以及 provider 侧触发的自动迁移(Telegram supergroup ID 变化、Slack channel ID 变化)。commands.debug: true会启用/debug(仅运行时覆盖)。commands.restart: true会启用/restart以及 gateway tool 的 restart action。commands.useAccessGroups: false允许命令绕过 access-group allowlists/policies。- Slash commands 与 directives 只会对已授权发送者生效。授权来源于 channel allowlists/pairing +
commands.useAccessGroups。
web (WhatsApp web channel runtime)
WhatsApp 通过 gateway 的 web channel(Baileys Web)运行。当存在已关联的会话时,它会自动启动。 如需默认保持关闭,将 web.enabled: false。
{
web: {
enabled: true,
heartbeatSeconds: 60,
reconnect: {
initialMs: 2000,
maxMs: 120000,
factor: 1.4,
jitter: 0.2,
maxAttempts: 0,
},
},
}channels.telegram (bot transport)
OpenClaw 只会在存在 channels.telegram 配置段时启动 Telegram。bot token 会从 channels.telegram.botToken(或 channels.telegram.tokenFile)解析;对默认账号,TELEGRAM_BOT_TOKEN 可作为兜底。 如需禁用自动启动,设置 channels.telegram.enabled: false。 多账号支持位于 channels.telegram.accounts(见上面的 multi-account 小节)。env tokens 只对默认账号生效。 如需阻止 Telegram 发起的 config 写入(包括 supergroup ID 迁移与 /config set|unset),设置 channels.telegram.configWrites: false。
{
channels: {
telegram: {
enabled: true,
botToken: "your-bot-token",
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["tg:123456789"], // optional; "open" requires ["*"]
groups: {
"*": { requireMention: true },
"-1001234567890": {
allowFrom: ["@admin"],
systemPrompt: "Keep answers brief.",
topics: {
"99": {
requireMention: false,
skills: ["search"],
systemPrompt: "Stay on topic.",
},
},
},
},
customCommands: [
{ command: "backup", description: "Git backup" },
{ command: "generate", description: "Create an image" },
],
historyLimit: 50, // include last N group messages as context (0 disables)
replyToMode: "first", // off | first | all
linkPreview: true, // toggle outbound link previews
streamMode: "partial", // off | partial | block (draft streaming; separate from block streaming)
draftChunk: {
// optional; only for streamMode=block
minChars: 200,
maxChars: 800,
breakPreference: "paragraph", // paragraph | newline | sentence
},
actions: { reactions: true, sendMessage: true }, // tool action gates (false disables)
reactionNotifications: "own", // off | own | all
mediaMaxMb: 5,
retry: {
// outbound retry policy
attempts: 3,
minDelayMs: 400,
maxDelayMs: 30000,
jitter: 0.1,
},
network: {
// transport overrides
autoSelectFamily: false,
},
proxy: "socks5://localhost:9050",
webhookUrl: "https://example.com/telegram-webhook", // requires webhookSecret
webhookSecret: "secret",
webhookPath: "/telegram-webhook",
},
},
}Draft streaming notes:
- Uses Telegram
sendMessageDraft(draft bubble, not a real message). - Requires private chat topics (message_thread_id in DMs; bot has topics enabled).
/reasoning streamstreams reasoning into the draft, then sends the final answer. Retry policy defaults and behavior are documented in Retry policy.
channels.discord (bot transport)
Configure the Discord bot by setting the bot token and optional gating: Multi-account support lives under channels.discord.accounts (see the multi-account section above). Env tokens only apply to the default account.
{
channels: {
discord: {
enabled: true,
token: "your-bot-token",
mediaMaxMb: 8, // clamp inbound media size
allowBots: false, // allow bot-authored messages
actions: {
// tool action gates (false disables)
reactions: true,
stickers: true,
polls: true,
permissions: true,
messages: true,
threads: true,
pins: true,
search: true,
memberInfo: true,
roleInfo: true,
roles: false,
channelInfo: true,
voiceStatus: true,
events: true,
moderation: false,
},
replyToMode: "off", // off | first | all
dm: {
enabled: true, // disable all DMs when false
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["1234567890", "steipete"], // optional DM allowlist ("open" requires ["*"])
groupEnabled: false, // enable group DMs
groupChannels: ["openclaw-dm"], // optional group DM allowlist
},
guilds: {
"123456789012345678": {
// guild id (preferred) or slug
slug: "friends-of-openclaw",
requireMention: false, // per-guild default
reactionNotifications: "own", // off | own | all | allowlist
users: ["987654321098765432"], // optional per-guild user allowlist
channels: {
general: { allow: true },
help: {
allow: true,
requireMention: true,
users: ["987654321098765432"],
skills: ["docs"],
systemPrompt: "Short answers only.",
},
},
},
},
historyLimit: 20, // include last N guild messages as context
textChunkLimit: 2000, // optional outbound text chunk size (chars)
chunkMode: "length", // optional chunking mode (length | newline)
maxLinesPerMessage: 17, // soft max lines per message (Discord UI clipping)
retry: {
// outbound retry policy
attempts: 3,
minDelayMs: 500,
maxDelayMs: 30000,
jitter: 0.1,
},
},
},
}OpenClaw 只会在存在 channels.discord 配置段时启动 Discord。token 会从 channels.discord.token 解析;对默认账号,DISCORD_BOT_TOKEN 可作为兜底(除非 channels.discord.enabled 为 false)。为 cron/CLI 命令指定投递目标时,请使用 user:<id>(DM)或 channel:<id>(guild channel);裸数字 ID 含义不明确,会被拒绝。 Guild slugs 为小写,并把空格替换为 -;channel keys 使用 slugged 的 channel 名(不带前导 #)。为避免重命名歧义,建议用 guild ids 作为 key。 默认会忽略 bot 自己发出的消息。可用 channels.discord.allowBots 启用(但仍会过滤自身消息以避免自回复循环)。 Reaction 通知模式:
off:不产生 reaction 事件。own:仅处理 bot 自己消息上的 reactions(默认)。all:处理所有消息上的所有 reactions。allowlist:仅处理来自guilds.<id>.users的 reactions(列表为空会禁用)。 出站文本会按channels.discord.textChunkLimit(默认 2000)分块。可设置channels.discord.chunkMode="newline",在按长度分块之前先按空行(段落边界)分割。Discord 客户端可能会裁剪特别高的消息,所以channels.discord.maxLinesPerMessage(默认 17)会在多行回复未超过 2000 字符时也主动切分。 重试策略的默认值与行为见 Retry policy。
channels.googlechat (Chat API webhook)
Google Chat runs over HTTP webhooks with app-level auth (service account). Multi-account support lives under channels.googlechat.accounts (see the multi-account section above). Env vars only apply to the default account.
{
channels: {
googlechat: {
enabled: true,
serviceAccountFile: "/path/to/service-account.json",
audienceType: "app-url", // app-url | project-number
audience: "https://gateway.example.com/googlechat",
webhookPath: "/googlechat",
botUser: "users/1234567890", // optional; improves mention detection
dm: {
enabled: true,
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["users/1234567890"], // optional; "open" requires ["*"]
},
groupPolicy: "allowlist",
groups: {
"spaces/AAAA": { allow: true, requireMention: true },
},
actions: { reactions: true },
typingIndicator: "message",
mediaMaxMb: 20,
},
},
}Notes:
- Service account JSON can be inline (
serviceAccount) or file-based (serviceAccountFile). - Env fallbacks for the default account:
GOOGLE_CHAT_SERVICE_ACCOUNTorGOOGLE_CHAT_SERVICE_ACCOUNT_FILE. audienceType+audiencemust match the Chat app’s webhook auth config.- Use
spaces/<spaceId>orusers/<userId|email>when setting delivery targets.
channels.slack (socket mode)
Slack runs in Socket Mode and requires both a bot token and app token:
{
channels: {
slack: {
enabled: true,
botToken: "xoxb-...",
appToken: "xapp-...",
dm: {
enabled: true,
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["U123", "U456", "*"], // optional; "open" requires ["*"]
groupEnabled: false,
groupChannels: ["G123"],
},
channels: {
C123: { allow: true, requireMention: true, allowBots: false },
"#general": {
allow: true,
requireMention: true,
allowBots: false,
users: ["U123"],
skills: ["docs"],
systemPrompt: "Short answers only.",
},
},
historyLimit: 50, // include last N channel/group messages as context (0 disables)
allowBots: false,
reactionNotifications: "own", // off | own | all | allowlist
reactionAllowlist: ["U123"],
replyToMode: "off", // off | first | all
thread: {
historyScope: "thread", // thread | channel
inheritParent: false,
},
actions: {
reactions: true,
messages: true,
pins: true,
memberInfo: true,
emojiList: true,
},
slashCommand: {
enabled: true,
name: "openclaw",
sessionPrefix: "slack:slash",
ephemeral: true,
},
textChunkLimit: 4000,
chunkMode: "length",
mediaMaxMb: 20,
},
},
}多账号支持位于 channels.slack.accounts(见上面的 multi-account 小节)。env tokens 只对默认账号生效。
当 provider 启用且两个 token 都已设置(通过 config 或 SLACK_BOT_TOKEN + SLACK_APP_TOKEN)时,OpenClaw 会启动 Slack。为 cron/CLI 命令指定投递目标时,请使用 user:<id>(DM)或 channel:<id>。 如需阻止 Slack 发起的 config 写入(包括 channel ID 迁移与 /config set|unset),设置 channels.slack.configWrites: false。
默认会忽略 bot 自己发出的消息。可用 channels.slack.allowBots 或 channels.slack.channels.<id>.allowBots 启用。
Reaction 通知模式:
off:不产生 reaction 事件。own:仅处理 bot 自己消息上的 reactions(默认)。all:处理所有消息上的所有 reactions。allowlist:仅处理来自channels.slack.reactionAllowlist的 reactions(列表为空会禁用)。
Thread session 隔离:
channels.slack.thread.historyScope控制 thread 历史是按 thread 隔离(thread,默认)还是在整个 channel 内共享(channel)。channels.slack.thread.inheritParent控制新 thread sessions 是否继承父 channel 的 transcript(默认:false)。
Slack action groups(用于 gate slack tool actions):
| Action group | 默认 | 说明 |
|---|---|---|
| reactions | enabled | 点赞 + 列出 reactions |
| messages | enabled | 读/发/编辑/删除 |
| pins | enabled | Pin/unpin/list |
| memberInfo | enabled | 成员信息 |
| emojiList | enabled | 自定义 emoji 列表 |
channels.mattermost (bot token)
Mattermost 以插件形式提供,不包含在核心安装中。 请先安装:openclaw plugins install @openclaw/mattermost(或在 git checkout 中使用 ./extensions/mattermost)。
Mattermost 需要 bot token 以及你的服务器 base URL:
{
channels: {
mattermost: {
enabled: true,
botToken: "mm-token",
baseUrl: "https://chat.example.com",
dmPolicy: "pairing",
chatmode: "oncall", // oncall | onmessage | onchar
oncharPrefixes: [">", "!"],
textChunkLimit: 4000,
chunkMode: "length",
},
},
}当账号已配置(bot token + base URL)且启用时,OpenClaw 会启动 Mattermost。token + base URL 会从默认账号的 channels.mattermost.botToken + channels.mattermost.baseUrl 解析;也可用 MATTERMOST_BOT_TOKEN + MATTERMOST_URL 作为兜底(除非 channels.mattermost.enabled 为 false)。
Chat modes:
oncall(默认):仅在被 @ 提及时响应频道消息。onmessage:响应每一条频道消息。onchar:当消息以触发前缀开头时响应(channels.mattermost.oncharPrefixes,默认[">", "!"])。
访问控制:
- 默认 DMs:
channels.mattermost.dmPolicy="pairing"(未知发送者会收到 pairing code)。 - 公开 DMs:
channels.mattermost.dmPolicy="open"并设置channels.mattermost.allowFrom=["*"]。 - Groups:默认
channels.mattermost.groupPolicy="allowlist"(mention gating)。可用channels.mattermost.groupAllowFrom限制发送者。
多账号支持位于 channels.mattermost.accounts(见上面的 multi-account 小节)。env vars 只对默认账号生效。 为投递目标指定时请使用 channel:<id> 或 user:<id>(或 @username);裸 ids 会被当作 channel ids。
channels.signal (signal-cli)
Signal reactions can emit system events (shared reaction tooling):
{
channels: {
signal: {
reactionNotifications: "own", // off | own | all | allowlist
reactionAllowlist: ["+15551234567", "uuid:123e4567-e89b-12d3-a456-426614174000"],
historyLimit: 50, // include last N group messages as context (0 disables)
},
},
}Reaction notification modes:
off: no reaction events.own: reactions on the bot's own messages (default).all: all reactions on all messages.allowlist: reactions fromchannels.signal.reactionAllowliston all messages (empty list disables).
channels.imessage (imsg CLI)
OpenClaw 会启动 imsg rpc(通过 stdio 的 JSON-RPC)。不需要 daemon 或端口。
{
channels: {
imessage: {
enabled: true,
cliPath: "imsg",
dbPath: "~/Library/Messages/chat.db",
remoteHost: "user@gateway-host", // SCP for remote attachments when using SSH wrapper
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["+15555550123", "user@example.com", "chat_id:123"],
historyLimit: 50, // include last N group messages as context (0 disables)
includeAttachments: false,
mediaMaxMb: 16,
service: "auto",
region: "US",
},
},
}多账号支持位于 channels.imessage.accounts(见上面的 multi-account 小节)。
备注:
- 需要对 Messages 数据库授予 Full Disk Access。
- 第一次发送会弹窗请求 Messages automation 权限。
- 优先使用
chat_id:<id>作为目标。可用imsg chats --limit 20列出 chats。 channels.imessage.cliPathcan point to a wrapper script (e.g.sshto another Mac that runsimsg rpc); use SSH keys to avoid password prompts.- For remote SSH wrappers, set
channels.imessage.remoteHostto fetch attachments via SCP whenincludeAttachmentsis enabled.
Example wrapper:
#!/usr/bin/env bash
exec ssh -T gateway-host imsg "$@"agents.defaults.workspace
Sets the single global workspace directory used by the agent for file operations.
Default: ~/.openclaw/workspace.
{
agents: { defaults: { workspace: "~/.openclaw/workspace" } },
}If agents.defaults.sandbox is enabled, non-main sessions can override this with their own per-scope workspaces under agents.defaults.sandbox.workspaceRoot.
agents.defaults.repoRoot
Optional repository root to show in the system prompt’s Runtime line. If unset, OpenClaw tries to detect a .git directory by walking upward from the workspace (and current working directory). The path must exist to be used.
{
agents: { defaults: { repoRoot: "~/Projects/openclaw" } },
}agents.defaults.skipBootstrap
Disables automatic creation of the workspace bootstrap files (AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, USER.md, and BOOTSTRAP.md).
Use this for pre-seeded deployments where your workspace files come from a repo.
{
agents: { defaults: { skipBootstrap: true } },
}agents.defaults.bootstrapMaxChars
Max characters of each workspace bootstrap file injected into the system prompt before truncation. Default: 20000.
When a file exceeds this limit, OpenClaw logs a warning and injects a truncated head/tail with a marker.
{
agents: { defaults: { bootstrapMaxChars: 20000 } },
}agents.defaults.userTimezone
Sets the user’s timezone for system prompt context (not for timestamps in message envelopes). If unset, OpenClaw uses the host timezone at runtime.
{
agents: { defaults: { userTimezone: "America/Chicago" } },
}agents.defaults.timeFormat
Controls the time format shown in the system prompt’s Current Date & Time section. Default: auto (OS preference).
{
agents: { defaults: { timeFormat: "auto" } }, // auto | 12 | 24
}messages
Controls inbound/outbound prefixes and optional ack reactions. See Messages for queueing, sessions, and streaming context.
{
messages: {
responsePrefix: "🦞", // or "auto"
ackReaction: "👀",
ackReactionScope: "group-mentions",
removeAckAfterReply: false,
},
}responsePrefix is applied to all outbound replies (tool summaries, block streaming, final replies) across channels unless already present.
If messages.responsePrefix is unset, no prefix is applied by default. WhatsApp self-chat replies are the exception: they default to [{identity.name}] when set, otherwise [openclaw], so same-phone conversations stay legible. Set it to "auto" to derive [{identity.name}] for the routed agent (when set).
Template variables
The responsePrefix string can include template variables that resolve dynamically:
| Variable | Description | Example |
|---|---|---|
{model} | Short model name | claude-opus-4-5, gpt-4o |
{modelFull} | Full model identifier | anthropic/claude-opus-4-5 |
{provider} | Provider name | anthropic, openai |
{thinkingLevel} | Current thinking level | high, low, off |
{identity.name} | Agent identity name | (same as "auto" mode) |
Variables are case-insensitive ({MODEL} = {model}). {think} is an alias for {thinkingLevel}. Unresolved variables remain as literal text.
{
messages: {
responsePrefix: "[{model} | think:{thinkingLevel}]",
},
}Example output: [claude-opus-4-5 | think:high] Here's my response...
WhatsApp inbound prefix is configured via channels.whatsapp.messagePrefix (deprecated: messages.messagePrefix). Default stays unchanged: "[openclaw]" when channels.whatsapp.allowFrom is empty, otherwise "" (no prefix). When using "[openclaw]", OpenClaw will instead use [{identity.name}] when the routed agent has identity.name set.
ackReaction sends a best-effort emoji reaction to acknowledge inbound messages on channels that support reactions (Slack/Discord/Telegram/Google Chat). Defaults to the active agent’s identity.emoji when set, otherwise "👀". Set it to "" to disable.
ackReactionScope controls when reactions fire:
group-mentions(default): only when a group/room requires mentions and the bot was mentionedgroup-all: all group/room messagesdirect: direct messages onlyall: all messages
removeAckAfterReply removes the bot’s ack reaction after a reply is sent (Slack/Discord/Telegram/Google Chat only). Default: false.
messages.tts
Enable text-to-speech for outbound replies. When on, OpenClaw generates audio using ElevenLabs or OpenAI and attaches it to responses. Telegram uses Opus voice notes; other channels send MP3 audio.
{
messages: {
tts: {
auto: "always", // off | always | inbound | tagged
mode: "final", // final | all (include tool/block replies)
provider: "elevenlabs",
summaryModel: "openai/gpt-4.1-mini",
modelOverrides: {
enabled: true,
},
maxTextLength: 4000,
timeoutMs: 30000,
prefsPath: "~/.openclaw/settings/tts.json",
elevenlabs: {
apiKey: "elevenlabs_api_key",
baseUrl: "https://api.elevenlabs.io",
voiceId: "voice_id",
modelId: "eleven_multilingual_v2",
seed: 42,
applyTextNormalization: "auto",
languageCode: "en",
voiceSettings: {
stability: 0.5,
similarityBoost: 0.75,
style: 0.0,
useSpeakerBoost: true,
speed: 1.0,
},
},
openai: {
apiKey: "openai_api_key",
model: "gpt-4o-mini-tts",
voice: "alloy",
},
},
},
}Notes:
messages.tts.autocontrols auto‑TTS (off,always,inbound,tagged)./tts off|always|inbound|taggedsets the per‑session auto mode (overrides config).messages.tts.enabledis legacy; doctor migrates it tomessages.tts.auto.prefsPathstores local overrides (provider/limit/summarize).maxTextLengthis a hard cap for TTS input; summaries are truncated to fit.summaryModeloverridesagents.defaults.model.primaryfor auto-summary.- Accepts
provider/modelor an alias fromagents.defaults.models.
- Accepts
modelOverridesenables model-driven overrides like[[tts:...]]tags (on by default)./tts limitand/tts summarycontrol per-user summarization settings.apiKeyvalues fall back toELEVENLABS_API_KEY/XI_API_KEYandOPENAI_API_KEY.elevenlabs.baseUrloverrides the ElevenLabs API base URL.elevenlabs.voiceSettingssupportsstability/similarityBoost/style(0..1),useSpeakerBoost, andspeed(0.5..2.0).
talk
Defaults for Talk mode (macOS/iOS/Android). Voice IDs fall back to ELEVENLABS_VOICE_ID or SAG_VOICE_ID when unset. apiKey falls back to ELEVENLABS_API_KEY (or the gateway’s shell profile) when unset. voiceAliases lets Talk directives use friendly names (e.g. "voice":"Clawd").
{
talk: {
voiceId: "elevenlabs_voice_id",
voiceAliases: {
Clawd: "EXAVITQu4vr4xnSDxMaL",
Roger: "CwhRBWXzGAHq8TQ4Fs17",
},
modelId: "eleven_v3",
outputFormat: "mp3_44100_128",
apiKey: "elevenlabs_api_key",
interruptOnSpeech: true,
},
}agents.defaults
Controls the embedded agent runtime (model/thinking/verbose/timeouts). agents.defaults.models defines the configured model catalog (and acts as the allowlist for /model). agents.defaults.model.primary sets the default model; agents.defaults.model.fallbacks are global failovers. agents.defaults.imageModel is optional and is only used if the primary model lacks image input. Each agents.defaults.models entry can include:
alias(optional model shortcut, e.g./opus).params(optional provider-specific API params passed through to the model request).
params is also applied to streaming runs (embedded agent + compaction). Supported keys today: temperature, maxTokens. These merge with call-time options; caller-supplied values win. temperature is an advanced knob—leave unset unless you know the model’s defaults and need a change.
Example:
{
agents: {
defaults: {
models: {
"anthropic/claude-sonnet-4-5-20250929": {
params: { temperature: 0.6 },
},
"openai/gpt-5.2": {
params: { maxTokens: 8192 },
},
},
},
},
}Z.AI GLM-4.x 模型会自动启用 thinking mode,除非你:
- 设置
--thinking off,或 - 自行定义
agents.defaults.models["zai/<model>"].params.thinking。
OpenClaw 也内置了一些 alias 缩写。注意:这些默认值只在该模型已存在于 agents.defaults.models 时才会生效:
opus->anthropic/claude-opus-4-5sonnet->anthropic/claude-sonnet-4-5gpt->openai/gpt-5.2gpt-mini->openai/gpt-5-minigemini->google/gemini-3-pro-previewgemini-flash->google/gemini-3-flash-preview
如果你自己配置了同名 alias(大小写不敏感),则以你的值为准(默认值不会覆盖)。
示例:primary 使用 Opus 4.5,fallback 使用 MiniMax M2.1(托管 MiniMax):
{
agents: {
defaults: {
models: {
"anthropic/claude-opus-4-5": { alias: "opus" },
"minimax/MiniMax-M2.1": { alias: "minimax" },
},
model: {
primary: "anthropic/claude-opus-4-5",
fallbacks: ["minimax/MiniMax-M2.1"],
},
},
},
}MiniMax auth: set MINIMAX_API_KEY (env) or configure models.providers.minimax.
agents.defaults.cliBackends (CLI fallback)
Optional CLI backends for text-only fallback runs (no tool calls). These are useful as a backup path when API providers fail. Image pass-through is supported when you configure an imageArg that accepts file paths.
Notes:
- CLI backends are text-first; tools are always disabled.
- Sessions are supported when
sessionArgis set; session ids are persisted per backend. - For
claude-cli, defaults are wired in. Override the command path if PATH is minimal (launchd/systemd).
Example:
{
agents: {
defaults: {
cliBackends: {
"claude-cli": {
command: "/opt/homebrew/bin/claude",
},
"my-cli": {
command: "my-cli",
args: ["--json"],
output: "json",
modelArg: "--model",
sessionArg: "--session",
sessionMode: "existing",
systemPromptArg: "--system",
systemPromptWhen: "first",
imageArg: "--image",
imageMode: "repeat",
},
},
},
},
}{
agents: {
defaults: {
models: {
"anthropic/claude-opus-4-5": { alias: "Opus" },
"anthropic/claude-sonnet-4-1": { alias: "Sonnet" },
"openrouter/deepseek/deepseek-r1:free": {},
"zai/glm-4.7": {
alias: "GLM",
params: {
thinking: {
type: "enabled",
clear_thinking: false,
},
},
},
},
model: {
primary: "anthropic/claude-opus-4-5",
fallbacks: [
"openrouter/deepseek/deepseek-r1:free",
"openrouter/meta-llama/llama-3.3-70b-instruct:free",
],
},
imageModel: {
primary: "openrouter/qwen/qwen-2.5-vl-72b-instruct:free",
fallbacks: ["openrouter/google/gemini-2.0-flash-vision:free"],
},
thinkingDefault: "low",
verboseDefault: "off",
elevatedDefault: "on",
timeoutSeconds: 600,
mediaMaxMb: 5,
heartbeat: {
every: "30m",
target: "last",
},
maxConcurrent: 3,
subagents: {
model: "minimax/MiniMax-M2.1",
maxConcurrent: 1,
archiveAfterMinutes: 60,
},
exec: {
backgroundMs: 10000,
timeoutSec: 1800,
cleanupMs: 1800000,
},
contextTokens: 200000,
},
},
}agents.defaults.contextPruning (tool-result pruning)
agents.defaults.contextPruning 会在请求发给 LLM 之前,针对内存上下文中的旧 tool results 做裁剪。 它不会修改磁盘上的 session 历史(*.jsonl 仍保持完整)。
该功能用于降低“话很多”的 agent 随时间累积大量 tool 输出后带来的 token 消耗。
概要:
- 永远不会触碰 user/assistant 消息。
- 保护最后
keepLastAssistants条 assistant 消息(从该点之后的 tool results 不会被裁剪)。 - 保护 bootstrap 前缀(第一条 user 消息之前的内容不会被裁剪)。
- 模式:
adaptive:当估算的 context ratio 超过softTrimRatio时,对超大的 tool result 做软裁剪(保留 head/tail)。 随后,当估算的 context ratio 超过hardClearRatio且可裁剪的 tool-result 体积足够(minPrunableToolChars)时,硬清理最旧的一批符合条件的 tool results。aggressive:总是把 cutoff 之前符合条件的 tool results 替换为hardClear.placeholder(不做 ratio 判断)。
软裁剪 vs 硬清理(会影响发给 LLM 的上下文内容):
- Soft-trim(软裁剪):只针对_超大_ tool results。保留开头 + 结尾,并在中间插入
...。- Before:
toolResult("…very long output…") - After:
toolResult("HEAD…\n...\n…TAIL\n\n[Tool result trimmed: …]")
- Before:
- Hard-clear(硬清理):把整个 tool result 替换为占位符。
- Before:
toolResult("…very long output…") - After:
toolResult("[Old tool result content cleared]")
- Before:
备注 / 当前限制:
- 目前包含图片 blocks 的 tool results 会被跳过(永不 trim/clear)。
- 估算的 “context ratio” 基于字符数(近似值),而不是精确 tokens。
- 如果 session 里还没有至少
keepLastAssistants条 assistant 消息,则跳过裁剪。 - 在
aggressive模式下会忽略hardClear.enabled(符合条件的 tool results 总会被替换为hardClear.placeholder)。
Default (adaptive):
{
agents: { defaults: { contextPruning: { mode: "adaptive" } } },
}To disable:
{
agents: { defaults: { contextPruning: { mode: "off" } } },
}Defaults (when mode is "adaptive" or "aggressive"):
keepLastAssistants:3softTrimRatio:0.3(adaptive only)hardClearRatio:0.5(adaptive only)minPrunableToolChars:50000(adaptive only)softTrim:{ maxChars: 4000, headChars: 1500, tailChars: 1500 }(adaptive only)hardClear:{ enabled: true, placeholder: "[Old tool result content cleared]" }
Example (aggressive, minimal):
{
agents: { defaults: { contextPruning: { mode: "aggressive" } } },
}Example (adaptive tuned):
{
agents: {
defaults: {
contextPruning: {
mode: "adaptive",
keepLastAssistants: 3,
softTrimRatio: 0.3,
hardClearRatio: 0.5,
minPrunableToolChars: 50000,
softTrim: { maxChars: 4000, headChars: 1500, tailChars: 1500 },
hardClear: { enabled: true, placeholder: "[Old tool result content cleared]" },
// Optional: restrict pruning to specific tools (deny wins; supports "*" wildcards)
tools: { deny: ["browser", "canvas"] },
},
},
},
}See /concepts/session-pruning for behavior details.
agents.defaults.compaction (reserve headroom + memory flush)
agents.defaults.compaction.mode selects the compaction summarization strategy. Defaults to default; set safeguard to enable chunked summarization for very long histories. See /concepts/compaction.
agents.defaults.compaction.reserveTokensFloor enforces a minimum reserveTokens value for Pi compaction (default: 20000). Set it to 0 to disable the floor.
agents.defaults.compaction.memoryFlush runs a silent agentic turn before auto-compaction, instructing the model to store durable memories on disk (e.g. memory/YYYY-MM-DD.md). It triggers when the session token estimate crosses a soft threshold below the compaction limit.
Legacy defaults:
memoryFlush.enabled:truememoryFlush.softThresholdTokens:4000memoryFlush.prompt/memoryFlush.systemPrompt: built-in defaults withNO_REPLY- Note: memory flush is skipped when the session workspace is read-only (
agents.defaults.sandbox.workspaceAccess: "ro"or"none").
Example (tuned):
{
agents: {
defaults: {
compaction: {
mode: "safeguard",
reserveTokensFloor: 24000,
memoryFlush: {
enabled: true,
softThresholdTokens: 6000,
systemPrompt: "Session nearing compaction. Store durable memories now.",
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store.",
},
},
},
},
}Block streaming:
agents.defaults.blockStreamingDefault:"on"/"off"(default off).- Channel overrides:
*.blockStreaming(and per-account variants) to force block streaming on/off. Non-Telegram channels require an explicit*.blockStreaming: trueto enable block replies. agents.defaults.blockStreamingBreak:"text_end"or"message_end"(default: text_end).agents.defaults.blockStreamingChunk: soft chunking for streamed blocks. Defaults to 800–1200 chars, prefers paragraph breaks (\n\n), then newlines, then sentences. Example:json5{ agents: { defaults: { blockStreamingChunk: { minChars: 800, maxChars: 1200 } } }, }agents.defaults.blockStreamingCoalesce: merge streamed blocks before sending. Defaults to{ idleMs: 1000 }and inheritsminCharsfromblockStreamingChunkwithmaxCharscapped to the channel text limit. Signal/Slack/Discord/Google Chat default tominChars: 1500unless overridden. Channel overrides:channels.whatsapp.blockStreamingCoalesce,channels.telegram.blockStreamingCoalesce,channels.discord.blockStreamingCoalesce,channels.slack.blockStreamingCoalesce,channels.mattermost.blockStreamingCoalesce,channels.signal.blockStreamingCoalesce,channels.imessage.blockStreamingCoalesce,channels.msteams.blockStreamingCoalesce,channels.googlechat.blockStreamingCoalesce(and per-account variants).agents.defaults.humanDelay: randomized pause between block replies after the first. Modes:off(default),natural(800–2500ms),custom(useminMs/maxMs). Per-agent override:agents.list[].humanDelay. Example:json5See /concepts/streaming for behavior + chunking details.{ agents: { defaults: { humanDelay: { mode: "natural" } } }, }
Typing indicators:
agents.defaults.typingMode:"never" | "instant" | "thinking" | "message". Defaults toinstantfor direct chats / mentions andmessagefor unmentioned group chats.session.typingMode: per-session override for the mode.agents.defaults.typingIntervalSeconds: how often the typing signal is refreshed (default: 6s).session.typingIntervalSeconds: per-session override for the refresh interval. See /concepts/typing-indicators for behavior details.
agents.defaults.model.primary should be set as provider/model (e.g. anthropic/claude-opus-4-5). Aliases come from agents.defaults.models.*.alias (e.g. Opus). If you omit the provider, OpenClaw currently assumes anthropic as a temporary deprecation fallback. Z.AI models are available as zai/<model> (e.g. zai/glm-4.7) and require ZAI_API_KEY (or legacy Z_AI_API_KEY) in the environment.
agents.defaults.heartbeat configures periodic heartbeat runs:
every: duration string (ms,s,m,h); default unit minutes. Default:30m. Set0mto disable.model: optional override model for heartbeat runs (provider/model).includeReasoning: whentrue, heartbeats will also deliver the separateReasoning:message when available (same shape as/reasoning on). Default:false.session: optional session key to control which session the heartbeat runs in. Default:main.to: optional recipient override (channel-specific id, e.g. E.164 for WhatsApp, chat id for Telegram).target: optional delivery channel (last,whatsapp,telegram,discord,slack,msteams,signal,imessage,none). Default:last.prompt: optional override for the heartbeat 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.). Overrides are sent verbatim; include aRead HEARTBEAT.mdline if you still want the file read.ackMaxChars: max chars allowed afterHEARTBEAT_OKbefore delivery (default: 300).
Per-agent heartbeats:
- Set
agents.list[].heartbeatto enable or override heartbeat settings for a specific agent. - If any agent entry defines
heartbeat, only those agents run heartbeats; defaults become the shared baseline for those agents.
Heartbeats run full agent turns. Shorter intervals burn more tokens; be mindful of every, keep HEARTBEAT.md tiny, and/or choose a cheaper model.
tools.exec configures background exec defaults:
backgroundMs: time before auto-background (ms, default 10000)timeoutSec: auto-kill after this runtime (seconds, default 1800)cleanupMs: how long to keep finished sessions in memory (ms, default 1800000)notifyOnExit: enqueue a system event + request heartbeat when backgrounded exec exits (default true)applyPatch.enabled: enable experimentalapply_patch(OpenAI/OpenAI Codex only; default false)applyPatch.allowModels: optional allowlist of model ids (e.g.gpt-5.2oropenai/gpt-5.2) Note:applyPatchis only undertools.exec.
tools.web configures web search + fetch tools:
tools.web.search.enabled(default: true when key is present)tools.web.search.apiKey(recommended: set viaopenclaw configure --section web, or useBRAVE_API_KEYenv var)tools.web.search.maxResults(1–10, default 5)tools.web.search.timeoutSeconds(default 30)tools.web.search.cacheTtlMinutes(default 15)tools.web.fetch.enabled(default true)tools.web.fetch.maxChars(default 50000)tools.web.fetch.timeoutSeconds(default 30)tools.web.fetch.cacheTtlMinutes(default 15)tools.web.fetch.userAgent(optional override)tools.web.fetch.readability(default true; disable to use basic HTML cleanup only)tools.web.fetch.firecrawl.enabled(default true when an API key is set)tools.web.fetch.firecrawl.apiKey(optional; defaults toFIRECRAWL_API_KEY)tools.web.fetch.firecrawl.baseUrl(default https://api.firecrawl.dev)tools.web.fetch.firecrawl.onlyMainContent(default true)tools.web.fetch.firecrawl.maxAgeMs(optional)tools.web.fetch.firecrawl.timeoutSeconds(optional)
tools.media configures inbound media understanding (image/audio/video):
tools.media.models: shared model list (capability-tagged; used after per-cap lists).tools.media.concurrency: max concurrent capability runs (default 2).tools.media.image/tools.media.audio/tools.media.video:enabled: opt-out switch (default true when models are configured).prompt: optional prompt override (image/video append amaxCharshint automatically).maxChars: max output characters (default 500 for image/video; unset for audio).maxBytes: max media size to send (defaults: image 10MB, audio 20MB, video 50MB).timeoutSeconds: request timeout (defaults: image 60s, audio 60s, video 120s).language: optional audio hint.attachments: attachment policy (mode,maxAttachments,prefer).scope: optional gating (first match wins) withmatch.channel,match.chatType, ormatch.keyPrefix.models: ordered list of model entries; failures or oversize media fall back to the next entry.
- Each
models[]entry:- Provider entry (
type: "provider"or omitted):provider: API provider id (openai,anthropic,google/gemini,groq, etc).model: model id override (required for image; defaults togpt-4o-mini-transcribe/whisper-large-v3-turbofor audio providers, andgemini-3-flash-previewfor video).profile/preferredProfile: auth profile selection.
- CLI entry (
type: "cli"):command: executable to run.args: templated args (supports,,, etc).
capabilities: optional list (image,audio,video) to gate a shared entry. Defaults when omitted:openai/anthropic/minimax→ image,google→ image+audio+video,groq→ audio.prompt,maxChars,maxBytes,timeoutSeconds,languagecan be overridden per entry.
- Provider entry (
If no models are configured (or enabled: false), understanding is skipped; the model still receives the original attachments.
Provider auth follows the standard model auth order (auth profiles, env vars like OPENAI_API_KEY/GROQ_API_KEY/GEMINI_API_KEY, or models.providers.*.apiKey).
Example:
{
tools: {
media: {
audio: {
enabled: true,
maxBytes: 20971520,
scope: {
default: "deny",
rules: [{ action: "allow", match: { chatType: "direct" } }],
},
models: [
{ provider: "openai", model: "gpt-4o-mini-transcribe" },
{ type: "cli", command: "whisper", args: ["--model", "base", "{{MediaPath}}"] },
],
},
video: {
enabled: true,
maxBytes: 52428800,
models: [{ provider: "google", model: "gemini-3-flash-preview" }],
},
},
},
}agents.defaults.subagents configures sub-agent defaults:
model: default model for spawned sub-agents (string or{ primary, fallbacks }). If omitted, sub-agents inherit the caller’s model unless overridden per agent or per call.maxConcurrent: max concurrent sub-agent runs (default 1)archiveAfterMinutes: auto-archive sub-agent sessions after N minutes (default 60; set0to disable)- Per-subagent tool policy:
tools.subagents.tools.allow/tools.subagents.tools.deny(deny wins)
tools.profile sets a base tool allowlist before tools.allow/tools.deny:
minimal:session_statusonlycoding:group:fs,group:runtime,group:sessions,group:memory,imagemessaging:group:messaging,sessions_list,sessions_history,sessions_send,session_statusfull: no restriction (same as unset)
Per-agent override: agents.list[].tools.profile.
Example (messaging-only by default, allow Slack + Discord tools too):
{
tools: {
profile: "messaging",
allow: ["slack", "discord"],
},
}Example (coding profile, but deny exec/process everywhere):
{
tools: {
profile: "coding",
deny: ["group:runtime"],
},
}tools.byProvider lets you further restrict tools for specific providers (or a single provider/model). Per-agent override: agents.list[].tools.byProvider.
Order: base profile → provider profile → allow/deny policies. Provider keys accept either provider (e.g. google-antigravity) or provider/model (e.g. openai/gpt-5.2).
Example (keep global coding profile, but minimal tools for Google Antigravity):
{
tools: {
profile: "coding",
byProvider: {
"google-antigravity": { profile: "minimal" },
},
},
}Example (provider/model-specific allowlist):
{
tools: {
allow: ["group:fs", "group:runtime", "sessions_list"],
byProvider: {
"openai/gpt-5.2": { allow: ["group:fs", "sessions_list"] },
},
},
}tools.allow / tools.deny configure a global tool allow/deny policy (deny wins). Matching is case-insensitive and supports * wildcards ("*" means all tools). This is applied even when the Docker sandbox is off.
Example (disable browser/canvas everywhere):
{
tools: { deny: ["browser", "canvas"] },
}Tool groups (shorthands) work in global and per-agent tool policies:
group:runtime:exec,bash,processgroup:fs:read,write,edit,apply_patchgroup:sessions:sessions_list,sessions_history,sessions_send,sessions_spawn,session_statusgroup:memory:memory_search,memory_getgroup:web:web_search,web_fetchgroup:ui:browser,canvasgroup:automation:cron,gatewaygroup:messaging:messagegroup:nodes:nodesgroup:openclaw: all built-in OpenClaw tools (excludes provider plugins)
tools.elevated controls elevated (host) exec access:
enabled: allow elevated mode (default true)allowFrom: per-channel allowlists (empty = disabled)whatsapp: E.164 numberstelegram: chat ids or usernamesdiscord: user ids or usernames (falls back tochannels.discord.dm.allowFromif omitted)signal: E.164 numbersimessage: handles/chat idswebchat: session ids or usernames
Example:
{
tools: {
elevated: {
enabled: true,
allowFrom: {
whatsapp: ["+15555550123"],
discord: ["steipete", "1234567890123"],
},
},
},
}Per-agent override (further restrict):
{
agents: {
list: [
{
id: "family",
tools: {
elevated: { enabled: false },
},
},
],
},
}备注:
tools.elevated是全局基线;agents.list[].tools.elevated只能进一步收紧(两者都必须允许才算允许)。/elevated on|off|ask|full会按 session key 存储状态;inline directives 只对单条消息生效。- Elevated
exec在宿主机上运行,并绕过 sandboxing。 - Tool policy 仍然生效;如果
exec被 deny,则无法使用 elevated。
agents.defaults.maxConcurrent sets the maximum number of embedded agent runs that can execute in parallel across sessions. Each session is still serialized (one run per session key at a time). Default: 1.
agents.defaults.sandbox
为内嵌 agent 提供可选的 Docker sandboxing。它主要面向 non-main sessions,用来避免它们访问你的宿主机系统。
详情:Sandboxing
默认值(启用时):
- scope:
"agent"(每个 agent 一个 container + workspace) - 基于 Debian bookworm-slim 的镜像
- agent workspace 访问:
workspaceAccess: "none"(默认)"none":使用~/.openclaw/sandboxes下按 scope 划分的 sandbox workspace
"ro":sandbox workspace 位于/workspace,并把 agent workspace 以只读方式挂载到/agent(会禁用write/edit/apply_patch)"rw":把 agent workspace 以读写方式挂载到/workspace
- auto-prune:idle > 24h 或 age > 7d
- tool policy:仅允许
exec、process、read、write、edit、apply_patch、sessions_list、sessions_history、sessions_send、sessions_spawn、session_status(deny 优先)- 通过
tools.sandbox.tools配置;可用agents.list[].tools.sandbox.tools做 per-agent 覆盖 - sandbox policy 支持 tool group 缩写:
group:runtime、group:fs、group:sessions、group:memory(见 Sandbox vs Tool Policy vs Elevated)
- 通过
- 可选 sandboxed browser(Chromium + CDP,noVNC observer)
- 加固开关:
network、user、pidsLimit、memory、cpus、ulimits、seccompProfile、apparmorProfile
警告:scope: "shared" 表示共享 container 与共享 workspace,没有 跨 session 隔离。若需要 per-session 隔离,请使用 scope: "session"。
Legacy:仍支持 perSession(true → scope: "session", false → scope: "shared")。
setupCommand 会在 container 创建完成后只运行 一次(在容器内通过 sh -lc 执行)。 如果要安装包,请确保允许网络出站、root 文件系统可写,并且使用 root 用户。
{
agents: {
defaults: {
sandbox: {
mode: "non-main", // off | non-main | all
scope: "agent", // session | agent | shared (agent is default)
workspaceAccess: "none", // none | ro | rw
workspaceRoot: "~/.openclaw/sandboxes",
docker: {
image: "openclaw-sandbox:bookworm-slim",
containerPrefix: "openclaw-sbx-",
workdir: "/workspace",
readOnlyRoot: true,
tmpfs: ["/tmp", "/var/tmp", "/run"],
network: "none",
user: "1000:1000",
capDrop: ["ALL"],
env: { LANG: "C.UTF-8" },
setupCommand: "apt-get update && apt-get install -y git curl jq",
// Per-agent override (multi-agent): agents.list[].sandbox.docker.*
pidsLimit: 256,
memory: "1g",
memorySwap: "2g",
cpus: 1,
ulimits: {
nofile: { soft: 1024, hard: 2048 },
nproc: 256,
},
seccompProfile: "/path/to/seccomp.json",
apparmorProfile: "openclaw-sandbox",
dns: ["1.1.1.1", "8.8.8.8"],
extraHosts: ["internal.service:10.0.0.5"],
binds: ["/var/run/docker.sock:/var/run/docker.sock", "/home/user/source:/source:rw"],
},
browser: {
enabled: false,
image: "openclaw-sandbox-browser:bookworm-slim",
containerPrefix: "openclaw-sbx-browser-",
cdpPort: 9222,
vncPort: 5900,
noVncPort: 6080,
headless: false,
enableNoVnc: true,
allowHostControl: false,
allowedControlUrls: ["http://10.0.0.42:18791"],
allowedControlHosts: ["browser.lab.local", "10.0.0.42"],
allowedControlPorts: [18791],
autoStart: true,
autoStartTimeoutMs: 12000,
},
prune: {
idleHours: 24, // 0 disables idle pruning
maxAgeDays: 7, // 0 disables max-age pruning
},
},
},
},
tools: {
sandbox: {
tools: {
allow: [
"exec",
"process",
"read",
"write",
"edit",
"apply_patch",
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"session_status",
],
deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"],
},
},
},
}用下面命令构建一次默认 sandbox 镜像:
scripts/sandbox-setup.sh备注:sandbox containers 默认 network: "none";如果 agent 需要出站访问,请把 agents.defaults.sandbox.docker.network 设为 "bridge"(或你的自定义 network)。
备注:入站附件会被暂存到当前 workspace 的 media/inbound/*。当 workspaceAccess: "rw" 时,这意味着文件会写入 agent workspace。
备注:docker.binds 会挂载额外的宿主机目录;全局与 per-agent 的 binds 会合并。
用下面命令构建可选的 browser 镜像:
scripts/sandbox-browser-setup.sh当 agents.defaults.sandbox.browser.enabled=true 时,browser tool 会使用 sandboxed Chromium 实例(CDP)。如果启用 noVNC(当 headless=false 时默认启用),noVNC URL 会被注入到 system prompt,以便 agent 引用。 这不要求主 config 中启用 browser.enabled;sandbox control URL 会按 session 注入。
agents.defaults.sandbox.browser.allowHostControl(默认:false)允许 sandboxed sessions 通过 browser tool(target: "host")显式指向宿主机 browser control server。 如果你希望严格 sandbox 隔离,请保持关闭。
远程控制的 allowlists:
allowedControlUrls:允许target: "custom"的精确 control URLs。allowedControlHosts:允许的主机名(仅 hostname,不含端口)。allowedControlPorts:允许的端口(默认:http=80,https=443)。 默认:所有 allowlists 都未设置(不做限制)。allowHostControl默认为 false。
models (custom providers + base URLs)
OpenClaw 使用 pi-coding-agent 的模型目录(model catalog)。你可以通过编写 ~/.openclaw/agents/<agentId>/agent/models.json,或在 OpenClaw config 的 models.providers 下定义同一份 schema,来添加自定义 providers (LiteLLM、本地 OpenAI 兼容服务、Anthropic 代理等)。 各 provider 的概览与示例见:/concepts/model-providers。
当配置中存在 models.providers 时,OpenClaw 会在启动时向 ~/.openclaw/agents/<agentId>/agent/ 写入/合并 models.json:
- 默认行为:merge(保留已有 providers,按名称覆盖)
- 设置
models.mode: "replace"可直接覆盖文件内容
通过 agents.defaults.model.primary(provider/model)选择模型。
{
agents: {
defaults: {
model: { primary: "custom-proxy/llama-3.1-8b" },
models: {
"custom-proxy/llama-3.1-8b": {},
},
},
},
models: {
mode: "merge",
providers: {
"custom-proxy": {
baseUrl: "http://localhost:4000/v1",
apiKey: "LITELLM_KEY",
api: "openai-completions",
models: [
{
id: "llama-3.1-8b",
name: "Llama 3.1 8B",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 32000,
},
],
},
},
},
}OpenCode Zen (multi-model proxy)
OpenCode Zen is a multi-model gateway with per-model endpoints. OpenClaw uses the built-in opencode provider from pi-ai; set OPENCODE_API_KEY (or OPENCODE_ZEN_API_KEY) from https://opencode.ai/auth.
Notes:
- Model refs use
opencode/<modelId>(example:opencode/claude-opus-4-5). - If you enable an allowlist via
agents.defaults.models, add each model you plan to use. - Shortcut:
openclaw onboard --auth-choice opencode-zen.
{
agents: {
defaults: {
model: { primary: "opencode/claude-opus-4-5" },
models: { "opencode/claude-opus-4-5": { alias: "Opus" } },
},
},
}Z.AI (GLM-4.7) — provider alias support
Z.AI models are available via the built-in zai provider. Set ZAI_API_KEY in your environment and reference the model by provider/model.
Shortcut: openclaw onboard --auth-choice zai-api-key.
{
agents: {
defaults: {
model: { primary: "zai/glm-4.7" },
models: { "zai/glm-4.7": {} },
},
},
}Notes:
z.ai/*andz-ai/*are accepted aliases and normalize tozai/*.- If
ZAI_API_KEYis missing, requests tozai/*will fail with an auth error at runtime. - Example error:
No API key found for provider "zai". - Z.AI’s general API endpoint is
https://api.z.ai/api/paas/v4. GLM coding requests use the dedicated Coding endpointhttps://api.z.ai/api/coding/paas/v4. The built-inzaiprovider uses the Coding endpoint. If you need the general endpoint, define a custom provider inmodels.providerswith the base URL override (see the custom providers section above). - Use a fake placeholder in docs/configs; never commit real API keys.
Moonshot AI (Kimi)
Use Moonshot's OpenAI-compatible endpoint:
{
env: { MOONSHOT_API_KEY: "sk-..." },
agents: {
defaults: {
model: { primary: "moonshot/kimi-k2.5" },
models: { "moonshot/kimi-k2.5": { alias: "Kimi K2.5" } },
},
},
models: {
mode: "merge",
providers: {
moonshot: {
baseUrl: "https://api.moonshot.ai/v1",
apiKey: "${MOONSHOT_API_KEY}",
api: "openai-completions",
models: [
{
id: "kimi-k2.5",
name: "Kimi K2.5",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 256000,
maxTokens: 8192,
},
],
},
},
},
}Notes:
- Set
MOONSHOT_API_KEYin the environment or useopenclaw onboard --auth-choice moonshot-api-key. - Model ref:
moonshot/kimi-k2.5. - Use
https://api.moonshot.cn/v1if you need the China endpoint.
Kimi Coding
Use Moonshot AI's Kimi Coding endpoint (Anthropic-compatible, built-in provider):
{
env: { KIMI_API_KEY: "sk-..." },
agents: {
defaults: {
model: { primary: "kimi-coding/k2p5" },
models: { "kimi-coding/k2p5": { alias: "Kimi K2.5" } },
},
},
}Notes:
- Set
KIMI_API_KEYin the environment or useopenclaw onboard --auth-choice kimi-code-api-key. - Model ref:
kimi-coding/k2p5.
Synthetic (Anthropic-compatible)
Use Synthetic's Anthropic-compatible endpoint:
{
env: { SYNTHETIC_API_KEY: "sk-..." },
agents: {
defaults: {
model: { primary: "synthetic/hf:MiniMaxAI/MiniMax-M2.1" },
models: { "synthetic/hf:MiniMaxAI/MiniMax-M2.1": { alias: "MiniMax M2.1" } },
},
},
models: {
mode: "merge",
providers: {
synthetic: {
baseUrl: "https://api.synthetic.new/anthropic",
apiKey: "${SYNTHETIC_API_KEY}",
api: "anthropic-messages",
models: [
{
id: "hf:MiniMaxAI/MiniMax-M2.1",
name: "MiniMax M2.1",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 192000,
maxTokens: 65536,
},
],
},
},
},
}Notes:
- Set
SYNTHETIC_API_KEYor useopenclaw onboard --auth-choice synthetic-api-key. - Model ref:
synthetic/hf:MiniMaxAI/MiniMax-M2.1. - Base URL should omit
/v1because the Anthropic client appends it.
Local models (LM Studio) — recommended setup
See /gateway/local-models for the current local guidance. TL;DR: run MiniMax M2.1 via LM Studio Responses API on serious hardware; keep hosted models merged for fallback.
MiniMax M2.1
Use MiniMax M2.1 directly without LM Studio:
{
agent: {
model: { primary: "minimax/MiniMax-M2.1" },
models: {
"anthropic/claude-opus-4-5": { alias: "Opus" },
"minimax/MiniMax-M2.1": { alias: "Minimax" },
},
},
models: {
mode: "merge",
providers: {
minimax: {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "${MINIMAX_API_KEY}",
api: "anthropic-messages",
models: [
{
id: "MiniMax-M2.1",
name: "MiniMax M2.1",
reasoning: false,
input: ["text"],
// Pricing: update in models.json if you need exact cost tracking.
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
contextWindow: 200000,
maxTokens: 8192,
},
],
},
},
},
}Notes:
- Set
MINIMAX_API_KEYenvironment variable or useopenclaw onboard --auth-choice minimax-api. - Available model:
MiniMax-M2.1(default). - Update pricing in
models.jsonif you need exact cost tracking.
Cerebras (GLM 4.6 / 4.7)
Use Cerebras via their OpenAI-compatible endpoint:
{
env: { CEREBRAS_API_KEY: "sk-..." },
agents: {
defaults: {
model: {
primary: "cerebras/zai-glm-4.7",
fallbacks: ["cerebras/zai-glm-4.6"],
},
models: {
"cerebras/zai-glm-4.7": { alias: "GLM 4.7 (Cerebras)" },
"cerebras/zai-glm-4.6": { alias: "GLM 4.6 (Cerebras)" },
},
},
},
models: {
mode: "merge",
providers: {
cerebras: {
baseUrl: "https://api.cerebras.ai/v1",
apiKey: "${CEREBRAS_API_KEY}",
api: "openai-completions",
models: [
{ id: "zai-glm-4.7", name: "GLM 4.7 (Cerebras)" },
{ id: "zai-glm-4.6", name: "GLM 4.6 (Cerebras)" },
],
},
},
},
}Notes:
- Use
cerebras/zai-glm-4.7for Cerebras; usezai/glm-4.7for Z.AI direct. - Set
CEREBRAS_API_KEYin the environment or config.
Notes:
- Supported APIs:
openai-completions,openai-responses,anthropic-messages,google-generative-ai - Use
authHeader: true+headersfor custom auth needs. - Override the agent config root with
OPENCLAW_AGENT_DIR(orPI_CODING_AGENT_DIR) if you wantmodels.jsonstored elsewhere (default:~/.openclaw/agents/main/agent).
session
Controls session scoping, reset policy, reset triggers, and where the session store is written.
{
session: {
scope: "per-sender",
dmScope: "main",
identityLinks: {
alice: ["telegram:123456789", "discord:987654321012345678"],
},
reset: {
mode: "daily",
atHour: 4,
idleMinutes: 60,
},
resetByType: {
thread: { mode: "daily", atHour: 4 },
dm: { mode: "idle", idleMinutes: 240 },
group: { mode: "idle", idleMinutes: 120 },
},
resetTriggers: ["/new", "/reset"],
// Default is already per-agent under ~/.openclaw/agents/<agentId>/sessions/sessions.json
// You can override with {agentId} templating:
store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
// Direct chats collapse to agent:<agentId>:<mainKey> (default: "main").
mainKey: "main",
agentToAgent: {
// Max ping-pong reply turns between requester/target (0–5).
maxPingPongTurns: 5,
},
sendPolicy: {
rules: [{ action: "deny", match: { channel: "discord", chatType: "group" } }],
default: "allow",
},
},
}Fields:
mainKey: direct-chat bucket key (default:"main"). Useful when you want to “rename” the primary DM thread without changingagentId.- Sandbox note:
agents.defaults.sandbox.mode: "non-main"uses this key to detect the main session. Any session key that does not matchmainKey(groups/channels) is sandboxed.
- Sandbox note:
dmScope: how DM sessions are grouped (default:"main").main: all DMs share the main session for continuity.per-peer: isolate DMs by sender id across channels.per-channel-peer: isolate DMs per channel + sender (recommended for multi-user inboxes).per-account-channel-peer: isolate DMs per account + channel + sender (recommended for multi-account inboxes).
identityLinks: map canonical ids to provider-prefixed peers so the same person shares a DM session across channels when usingper-peer,per-channel-peer, orper-account-channel-peer.- Example:
alice: ["telegram:123456789", "discord:987654321012345678"].
- Example:
reset: primary reset policy. Defaults to daily resets at 4:00 AM local time on the gateway host.mode:dailyoridle(default:dailywhenresetis present).atHour: local hour (0-23) for the daily reset boundary.idleMinutes: sliding idle window in minutes. When daily + idle are both configured, whichever expires first wins.
resetByType: per-session overrides fordm,group, andthread.- If you only set legacy
session.idleMinuteswithout anyreset/resetByType, OpenClaw stays in idle-only mode for backward compatibility.
- If you only set legacy
heartbeatIdleMinutes: optional idle override for heartbeat checks (daily reset still applies when enabled).agentToAgent.maxPingPongTurns: max reply-back turns between requester/target (0–5, default 5).sendPolicy.default:allowordenyfallback when no rule matches.sendPolicy.rules[]: match bychannel,chatType(direct|group|room), orkeyPrefix(e.g.cron:). First deny wins; otherwise allow.
skills (skills config)
Controls bundled allowlist, install preferences, extra skill folders, and per-skill overrides. Applies to bundled skills and ~/.openclaw/skills (workspace skills still win on name conflicts).
Fields:
allowBundled: optional allowlist for bundled skills only. If set, only those bundled skills are eligible (managed/workspace skills unaffected).load.extraDirs: additional skill directories to scan (lowest precedence).install.preferBrew: prefer brew installers when available (default: true).install.nodeManager: node installer preference (npm|pnpm|yarn, default: npm).entries.<skillKey>: per-skill config overrides.
Per-skill fields:
enabled: setfalseto disable a skill even if it’s bundled/installed.env: environment variables injected for the agent run (only if not already set).apiKey: optional convenience for skills that declare a primary env var (e.g.nano-banana-pro→GEMINI_API_KEY).
Example:
{
skills: {
allowBundled: ["gemini", "peekaboo"],
load: {
extraDirs: ["~/Projects/agent-scripts/skills", "~/Projects/oss/some-skill-pack/skills"],
},
install: {
preferBrew: true,
nodeManager: "npm",
},
entries: {
"nano-banana-pro": {
apiKey: "GEMINI_KEY_HERE",
env: {
GEMINI_API_KEY: "GEMINI_KEY_HERE",
},
},
peekaboo: { enabled: true },
sag: { enabled: false },
},
},
}plugins (extensions)
Controls plugin discovery, allow/deny, and per-plugin config. Plugins are loaded from ~/.openclaw/extensions, <workspace>/.openclaw/extensions, plus any plugins.load.paths entries. Config changes require a gateway restart. See /plugin for full usage.
Fields:
enabled: master toggle for plugin loading (default: true).allow: optional allowlist of plugin ids; when set, only listed plugins load.deny: optional denylist of plugin ids (deny wins).load.paths: extra plugin files or directories to load (absolute or~).entries.<pluginId>: per-plugin overrides.enabled: setfalseto disable.config: plugin-specific config object (validated by the plugin if provided).
Example:
{
plugins: {
enabled: true,
allow: ["voice-call"],
load: {
paths: ["~/Projects/oss/voice-call-extension"],
},
entries: {
"voice-call": {
enabled: true,
config: {
provider: "twilio",
},
},
},
},
}browser (openclaw-managed browser)
OpenClaw can start a dedicated, isolated Chrome/Brave/Edge/Chromium instance for openclaw and expose a small loopback control service. Profiles can point at a remote Chromium-based browser via profiles.<name>.cdpUrl. Remote profiles are attach-only (start/stop/reset are disabled).
browser.cdpUrl remains for legacy single-profile configs and as the base scheme/host for profiles that only set cdpPort.
Defaults:
- enabled:
true - evaluateEnabled:
true(setfalseto disableact:evaluateandwait --fn) - control service: loopback only (port derived from
gateway.port, default18791) - CDP URL:
http://127.0.0.1:18792(control service + 1, legacy single-profile) - profile color:
#FF4500(lobster-orange) - Note: the control server is started by the running gateway (OpenClaw.app menubar, or
openclaw gateway). - Auto-detect order: default browser if Chromium-based; otherwise Chrome → Brave → Edge → Chromium → Chrome Canary.
{
browser: {
enabled: true,
evaluateEnabled: true,
// cdpUrl: "http://127.0.0.1:18792", // legacy single-profile override
defaultProfile: "chrome",
profiles: {
openclaw: { cdpPort: 18800, color: "#FF4500" },
work: { cdpPort: 18801, color: "#0066CC" },
remote: { cdpUrl: "http://10.0.0.42:9222", color: "#00AA00" },
},
color: "#FF4500",
// Advanced:
// headless: false,
// noSandbox: false,
// executablePath: "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
// attachOnly: false, // set true when tunneling a remote CDP to localhost
},
}ui(外观)
可选的强调色,供原生应用用于 UI 视觉元素(例如 Talk 模式气泡的色调)。
如果未设置,客户端会回退到较柔和的浅蓝色。
{
ui: {
seamColor: "#FF4500", // hex (RRGGBB or #RRGGBB)
// Optional: Control UI assistant identity override.
// If unset, the Control UI uses the active agent identity (config or IDENTITY.md).
assistant: {
name: "OpenClaw",
avatar: "CB", // emoji, short text, or image URL/data URI
},
},
}gateway(Gateway 服务模式 + bind)
使用 gateway.mode 明确声明这台机器是否应运行 Gateway。
默认值:
- mode:unset(视为“不要自动启动”)
- bind:
loopback - port:
18789(WS + HTTP 共用单一端口)
{
gateway: {
mode: "local", // or "remote"
port: 18789, // WS + HTTP multiplex
bind: "loopback",
// controlUi: { enabled: true, basePath: "/openclaw" }
// auth: { mode: "token", token: "your-token" } // token gates WS + Control UI access
// tailscale: { mode: "off" | "serve" | "funnel" }
},
}Control UI base path:
gateway.controlUi.basePath设置 Control UI 对外提供服务的 URL 前缀。- 例如:
"/ui"、"/openclaw"、"/apps/openclaw"。 - 默认:根路径(
/)(保持不变)。 gateway.controlUi.allowInsecureAuth:当省略 device identity(通常是走 HTTP)时,允许 Control UI 仅使用 token 认证。默认false。优先使用 HTTPS(Tailscale Serve)或127.0.0.1。gateway.controlUi.dangerouslyDisableDeviceAuth:禁用 Control UI 的 device identity 检查(仅 token/password)。默认false。仅用于紧急破窗场景。
相关文档:
可信代理(Trusted proxies):
gateway.trustedProxies:终止 TLS 并代理到 Gateway 的反向代理 IP 列表。- 当连接来自这些 IP 之一时,OpenClaw 会使用
x-forwarded-for(或x-real-ip)来确定客户端 IP,用于本地 pairing 检查以及 HTTP auth/local 检查。 - 只应列出你完全可控的代理,并确保它们会 覆盖 传入的
x-forwarded-for。
说明:
openclaw gateway除非gateway.mode设为local(或你传入 override flag),否则会拒绝启动。gateway.port控制 WebSocket + HTTP(control UI、hooks、A2UI)共用的单一复用端口。- OpenAI Chat Completions 端点:默认关闭;用
gateway.http.endpoints.chatCompletions.enabled: true启用。 - 优先级:
--port>OPENCLAW_GATEWAY_PORT>gateway.port> 默认18789。 - 默认要求 Gateway 认证(token/password 或 Tailscale Serve identity)。非 loopback 绑定必须配置共享 token/password。
- onboarding wizard 默认会生成一个 gateway token(即使绑定在 loopback)。
gateway.remote.token仅 用于远程 CLI 调用;它不会启用本地 gateway auth。gateway.token会被忽略。
认证与 Tailscale:
gateway.auth.mode设置握手认证方式(token或password)。未设置时默认认为是 token 认证。gateway.auth.token保存 token 认证的共享 token(同机 CLI 会使用)。- 一旦设置
gateway.auth.mode,就只接受该方式(外加可选的 Tailscale headers)。 gateway.auth.password可在此处设置,或通过OPENCLAW_GATEWAY_PASSWORD设置(推荐)。gateway.auth.allowTailscale允许使用 Tailscale Serve identity headers(tailscale-user-login)在以下条件下满足认证:请求到达 loopback,且带有x-forwarded-for、x-forwarded-proto、x-forwarded-host。OpenClaw 会在接受前通过tailscale whois解析x-forwarded-for对应的地址来验证身份。为true时,Serve 请求不需要 token/password;设为false则要求显式凭据。默认在tailscale.mode = "serve"且 auth mode 不是password时为true。gateway.tailscale.mode: "serve"使用 Tailscale Serve(仅 tailnet、loopback bind)。gateway.tailscale.mode: "funnel"将 dashboard 对公网暴露;仍需要认证。gateway.tailscale.resetOnExit在退出时重置 Serve/Funnel 配置。
远程客户端默认值(CLI):
gateway.remote.url:当gateway.mode = "remote"时,设置 CLI 调用默认使用的 Gateway WebSocket URL。gateway.remote.transport:选择 macOS 远程传输方式(默认ssh;direct对应 ws/wss)。当为direct时,gateway.remote.url必须是ws://或wss://;ws://host默认端口为18789。gateway.remote.token:远程调用的 token(不设置表示不使用 token 认证)。gateway.remote.password:远程调用的 password(不设置表示不使用 password 认证)。
macOS app 行为:
- OpenClaw.app 会监听
~/.openclaw/openclaw.json;当gateway.mode或gateway.remote.url变化时会实时切换模式。 - 若
gateway.mode未设置但设置了gateway.remote.url,macOS app 会将其视为 remote mode。 - 当你在 macOS app 中切换连接模式时,它会把
gateway.mode(以及 remote 模式下的gateway.remote.url+gateway.remote.transport)写回配置文件。
{
gateway: {
mode: "remote",
remote: {
url: "ws://gateway.tailnet:18789",
token: "your-token",
password: "your-password",
},
},
}Direct transport 示例(macOS app):
{
gateway: {
mode: "remote",
remote: {
transport: "direct",
url: "wss://gateway.example.ts.net",
token: "your-token",
},
},
}gateway.reload(配置热重载)
Gateway 会监听 ~/.openclaw/openclaw.json(或 OPENCLAW_CONFIG_PATH),并自动应用配置变更。
模式:
hybrid(默认):对安全变更进行热应用;对关键变更重启 Gateway。hot:只应用热更新安全的变更;当需要重启时记录日志。restart:任何配置变更都重启 Gateway。off:禁用热重载。
{
gateway: {
reload: {
mode: "hybrid",
debounceMs: 300,
},
},
}热重载矩阵(文件 + 影响)
被监听的文件:
~/.openclaw/openclaw.json(或OPENCLAW_CONFIG_PATH)
可热应用(无需完整重启 Gateway):
hooks(webhook auth/path/mappings)+hooks.gmail(重启 Gmail watcher)browser(重启 browser control server)cron(重启 cron 服务 + 更新并发度)agents.defaults.heartbeat(重启 heartbeat runner)web(重启 WhatsApp web channel)telegram、discord、signal、imessage(重启通道)agent、models、routing、messages、session、whatsapp、logging、skills、ui、talk、identity、wizard(动态读取)
需要完整重启 Gateway:
gateway(port/bind/auth/control UI/tailscale)bridge(legacy)discoverycanvasHostplugins- 任何未知/不支持的配置路径(为安全起见默认重启)
多实例隔离
要在同一台主机上运行多个 gateway(用于冗余或“救援机器人”),请隔离每个实例的 state + config,并使用不同端口:
OPENCLAW_CONFIG_PATH(按实例区分的配置)OPENCLAW_STATE_DIR(sessions/creds)agents.defaults.workspace(memories)gateway.port(每实例唯一)
便捷参数(CLI):
openclaw --dev …→ 使用~/.openclaw-dev并从基准19001派生端口openclaw --profile <name> …→ 使用~/.openclaw-<name>(端口由 config/env/flags 决定)
派生端口映射请见 Gateway runbook(gateway/browser/canvas)。 浏览器/CDP 端口隔离细节请见 Multiple gateways。
示例:
OPENCLAW_CONFIG_PATH=~/.openclaw/a.json \
OPENCLAW_STATE_DIR=~/.openclaw-a \
openclaw gateway --port 19001hooks(Gateway Webhooks)
在 Gateway HTTP server 上启用一个简单的 HTTP webhook 端点。
默认值:
- enabled:
false - path:
/hooks - maxBodyBytes:
262144(256 KB)
{
hooks: {
enabled: true,
token: "shared-secret",
path: "/hooks",
presets: ["gmail"],
transformsDir: "~/.openclaw/hooks",
mappings: [
{
match: { path: "gmail" },
action: "agent",
wakeMode: "now",
name: "Gmail",
sessionKey: "hook:gmail:{{messages[0].id}}",
messageTemplate: "From: {{messages[0].from}}\nSubject: {{messages[0].subject}}\n{{messages[0].snippet}}",
deliver: true,
channel: "last",
model: "openai/gpt-5.2-mini",
},
],
},
}请求必须包含 hook token:
Authorization: Bearer <token>或x-openclaw-token: <token>或?token=<token>
端点:
POST /hooks/wake→{ text, mode?: "now"|"next-heartbeat" }POST /hooks/agent→{ message, name?, sessionKey?, wakeMode?, deliver?, channel?, to?, model?, thinking?, timeoutSeconds? }POST /hooks/<name>→ 通过hooks.mappings解析
/hooks/agent 总会把一条摘要发到主会话中(并且在 wakeMode: "now" 时,可选触发一次立即 heartbeat)。
映射说明:
match.path匹配/hooks之后的子路径(例如/hooks/gmail→gmail)。match.source匹配 payload 字段(例如{ source: "gmail" }),这样你可以使用通用的/hooks/ingest路径。{{messages[0].subject}}这类模板从 payload 中读取。transform可以指向一个返回 hook action 的 JS/TS 模块。deliver: true会把最终回复发送到某个通道;channel默认为last(回退到 WhatsApp)。- 如果之前没有可用的投递路由,请显式设置
channel+to(Telegram/Discord/Google Chat/Slack/Signal/iMessage/MS Teams 必需)。 model为本次 hook 运行覆盖 LLM(provider/model或别名;若设置了agents.defaults.models,则必须在 allowlist 中)。
Gmail 辅助配置(由 openclaw webhooks gmail setup / run 使用):
{
hooks: {
gmail: {
account: "openclaw@gmail.com",
topic: "projects/<project-id>/topics/gog-gmail-watch",
subscription: "gog-gmail-watch-push",
pushToken: "shared-push-token",
hookUrl: "http://127.0.0.1:18789/hooks/gmail",
includeBody: true,
maxBytes: 20000,
renewEveryMinutes: 720,
serve: { bind: "127.0.0.1", port: 8788, path: "/" },
tailscale: { mode: "funnel", path: "/gmail-pubsub" },
// Optional: use a cheaper model for Gmail hook processing
// Falls back to agents.defaults.model.fallbacks, then primary, on auth/rate-limit/timeout
model: "openrouter/meta-llama/llama-3.3-70b-instruct:free",
// Optional: default thinking level for Gmail hooks
thinking: "off",
},
},
}Gmail hooks 的模型覆盖:
hooks.gmail.model指定处理 Gmail hook 时使用的模型(默认使用会话 primary)。- 可接受
provider/model引用,或来自agents.defaults.models的别名。 - 在认证/限流/超时等失败时,依次回退到
agents.defaults.model.fallbacks,再到agents.defaults.model.primary。 - 若设置了
agents.defaults.models,请把 hooks 模型也加入 allowlist。 - 启动时,如果配置的模型不在模型目录或 allowlist 中,会输出告警。
hooks.gmail.thinking设置 Gmail hooks 的默认 thinking 级别,可被每个 hook 的thinking覆盖。
Gateway 自动启动:
- 当
hooks.enabled=true且设置了hooks.gmail.account时,Gateway 会在启动时运行gog gmail watch serve,并自动续订 watch。 - 设置
OPENCLAW_SKIP_GMAIL_WATCHER=1可关闭自动启动(用于手动运行)。 - 避免在 Gateway 旁边再单独运行一个
gog gmail watch serve;否则会因端口冲突失败:listen tcp 127.0.0.1:8788: bind: address already in use。
注意:当 tailscale.mode 开启时,OpenClaw 默认将 serve.path 设为 /,以便 Tailscale 能正确代理 /gmail-pubsub(它会剥离 set-path 前缀)。 如果你需要后端收到带前缀的路径,请把 hooks.gmail.tailscale.target 设为完整 URL(并同步对齐 serve.path)。
canvasHost(LAN/tailnet Canvas 文件服务器 + 实时重载)
Gateway 通过 HTTP 提供一个 HTML/CSS/JS 目录,让 iOS/Android 节点可以直接 canvas.navigate 到它。
默认根目录:~/.openclaw/workspace/canvas
默认端口:18793(为避开 openclaw browser CDP 端口 18792)
服务器监听在 gateway bind host(LAN 或 Tailnet)上,以便节点可达。
该服务器会:
- 提供
canvasHost.root下的文件 - 在输出的 HTML 中注入一个极小的 live-reload 客户端
- 监听目录,并通过
/__openclaw__/ws的 WebSocket 端点广播重载 - 当目录为空时自动创建一个起始
index.html(让你立刻看到页面) - 同时在
/__openclaw__/a2ui/提供 A2UI,并向节点公布为canvasHostUrl(节点的 Canvas/A2UI 始终使用它)
如果目录很大或遇到 EMFILE,可以关闭 live reload(同时也会关闭文件监听):
- 配置:
canvasHost: { liveReload: false }
{
canvasHost: {
root: "~/.openclaw/workspace/canvas",
port: 18793,
liveReload: true,
},
}修改 canvasHost.* 需要重启 gateway(config reload 会触发重启)。
可通过以下方式禁用:
- 配置:
canvasHost: { enabled: false } - 环境变量:
OPENCLAW_SKIP_CANVAS_HOST=1
bridge(旧版 TCP bridge,已移除)
当前构建已不再包含 TCP bridge listener;bridge.* 配置 key 会被忽略。 节点通过 Gateway WebSocket 连接。该章节保留仅供历史参考。
旧版行为:
- Gateway 可以为节点(iOS/Android)暴露一个简单的 TCP bridge,通常在端口
18790。
默认值:
- enabled:
true - port:
18790 - bind:
lan(绑定到0.0.0.0)
绑定模式:
lan:0.0.0.0(任意网卡可达,包括 LAN/Wi‑Fi 与 Tailscale)tailnet:仅绑定到机器的 Tailscale IP(推荐用于 Vienna ⇄ London)loopback:127.0.0.1(仅本机)auto:优先 tailnet IP;若不存在则用lan
TLS:
bridge.tls.enabled:为 bridge 连接启用 TLS(启用后仅接受 TLS)。bridge.tls.autoGenerate:当缺少 cert/key 时生成自签证书(默认 true)。bridge.tls.certPath/bridge.tls.keyPath:bridge 证书与私钥的 PEM 路径。bridge.tls.caPath:可选的 PEM CA bundle(自定义根或将来 mTLS)。
当 TLS 启用时,Gateway 会在 discovery TXT 记录中公布 bridgeTls=1 与 bridgeTlsSha256,用于让节点 pin 证书。 手动连接在未存储 fingerprint 时使用 trust-on-first-use。 自动生成证书要求 openssl 在 PATH 中;若生成失败,bridge 将无法启动。
{
bridge: {
enabled: true,
port: 18790,
bind: "tailnet",
tls: {
enabled: true,
// Uses ~/.openclaw/bridge/tls/bridge-{cert,key}.pem when omitted.
// certPath: "~/.openclaw/bridge/tls/bridge-cert.pem",
// keyPath: "~/.openclaw/bridge/tls/bridge-key.pem"
},
},
}discovery.mdns(Bonjour / mDNS 广播模式)
控制 LAN mDNS 发现广播(_openclaw-gw._tcp)。
minimal(默认):在 TXT 记录中省略cliPath+sshPortfull:在 TXT 记录中包含cliPath+sshPortoff:完全禁用 mDNS 广播- Hostname:默认
openclaw(会公布openclaw.local)。可用OPENCLAW_MDNS_HOSTNAME覆盖。
{
discovery: { mdns: { mode: "minimal" } },
}discovery.wideArea(Wide-Area Bonjour / 单播 DNS‑SD)
启用后,Gateway 会基于配置的 discovery domain(例如 openclaw.internal.),在 ~/.openclaw/dns/ 下写入 _openclaw-gw._tcp 的单播 DNS‑SD zone。
要让 iOS/Android 跨网络发现(Vienna ⇄ London),需要同时配合:
- 在 gateway 主机上运行一个 DNS 服务器来提供你选择的域(推荐 CoreDNS)
- 配置 Tailscale split DNS,让客户端通过 gateway DNS 服务器解析该域
一次性设置助手(gateway 主机):
openclaw dns setup --apply{
discovery: { wideArea: { enabled: true } },
}模板变量
模板占位符会在 tools.media.*.models[].args 与 tools.media.models[].args 中展开(以及未来任何模板化参数字段)。
| Variable | Description | | ------------------ | ------------------------------------------------------------------------------- | -------- | ------- | ---------- | ----- | ------ | -------- | ------- | ------- | --- | | | 完整的入站消息正文 | | | 原始入站消息正文(不含历史/发送者包装;最适合命令解析) | | | 已去除群组提及的正文(对 agent 的最佳默认值) | | | 发送者标识(WhatsApp 为 E.164;不同 channel 可能不同) | | | 目标标识 | | | 通道消息 id(若可用) | | | 当前会话 UUID | | | "true" 表示创建了新会话 | | | 入站媒体伪 URL(若存在) | | | 本地媒体路径(若已下载) | | | 媒体类型(image/audio/document/…) | | | 音频转写文本(若启用) | | | 用于 CLI 条目的解析后媒体 prompt | | | 用于 CLI 条目的解析后最大输出字符数 | | | "direct" 或 "group" | | | 群组主题(尽力获取) | | | 群组成员预览(尽力获取) | | | 发送者显示名(尽力获取) | | | 发送者电话号码(尽力获取) | | | Provider 提示(whatsapp | telegram | discord | googlechat | slack | signal | imessage | msteams | webchat | …) |
Cron(Gateway 调度器)
Cron 是 Gateway 自带的调度器,用于唤醒与定时任务。功能概览与 CLI 示例请见 Cron jobs。
{
cron: {
enabled: true,
maxConcurrentRuns: 2,
},
}下一页:Agent Runtime 🦞