Skip to content

配置 🔧

OpenClaw 会从 ~/.openclaw/openclaw.json 读取一个可选的 JSON5 配置(允许注释与尾随逗号)。

如果该文件不存在,OpenClaw 会使用相对安全的默认值(内置 Pi agent + 按发送者分会话 + 工作区 ~/.openclaw/workspace)。一般你只在以下场景需要写配置:

  • 限制谁可以触发 bot(channels.whatsapp.allowFromchannels.telegram.allowFrom 等)
  • 控制群聊 allowlist 与提及触发行为(channels.whatsapp.groupschannels.telegram.groupschannels.discord.guildsagents.list[].groupChat
  • 自定义消息前缀(messages
  • 设置 agent 的工作区(agents.defaults.workspaceagents.list[].workspace
  • 调整内置 agent 默认值(agents.defaults)与会话行为(session
  • 设置每个 agent 的 identity(agents.list[].identity

第一次写配置? 建议先看 Configuration Examples,里面有完整示例与详细讲解。

严格配置校验

OpenClaw 只接受 完全匹配 schema 的配置。 未知 key、类型错误或无效值都会导致 Gateway 为安全起见 拒绝启动

当校验失败时:

  • Gateway 不会启动。
  • 只允许执行诊断相关命令(例如:openclaw doctoropenclaw logsopenclaw healthopenclaw statusopenclaw serviceopenclaw 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.patchopenclaw config set。建议保留 ~/.openclaw/openclaw.json 的备份。

参数:

  • raw(string)— 整份配置的 JSON5 payload
  • baseHash(可选)— 来自 config.get 的 config hash(当配置已存在时必需)
  • sessionKey(可选)— 用于 wake-up ping 的最近活跃 session key
  • note(可选)— 写入 restart sentinel 的备注
  • restartDelayMs(可选)— 重启前延迟(默认 2000)

示例(通过 gateway call):

bash
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 payload
  • baseHash(必需)— 来自 config.get 的 config hash
  • sessionKey(可选)— 用于 wake-up ping 的最近活跃 session key
  • note(可选)— 写入 restart sentinel 的备注
  • restartDelayMs(可选)— 重启前延迟(默认 2000)

示例:

bash
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
}'

最小配置(推荐起点)

json5
{
  agents: { defaults: { workspace: "~/.openclaw/workspace" } },
  channels: { whatsapp: { allowFrom: ["+15555550123"] } },
}

构建一次默认镜像:

bash
scripts/sandbox-setup.sh

自聊模式(推荐用于群聊控制)

为防止 bot 在群聊里对 WhatsApp 的 @ 提及自动回复(只对特定文本触发做出响应):

json5
{
  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

json5
// ~/.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"],
  },
}
json5
// ~/.openclaw/agents.json5
{
  defaults: { sandbox: { mode: "all", scope: "session" } },
  list: [{ id: "main", workspace: "~/.openclaw/workspace" }],
}

合并行为

  • 单文件:替换包含 $include 的对象
  • 文件数组:按顺序深度合并(后面的文件覆盖前面的文件)
  • 带同级 key:在包含合并完成后再合并同级 key(覆盖包含进来的值)
  • 同级 key + 数组/原始值:不支持(被包含的内容必须是对象)
json5
// Sibling keys override included values
{
  $include: "./base.json5", // { a: 1, b: 2 }
  b: 99, // Result: { a: 1, b: 99 }
}

Nested includes

被包含的文件也可以继续包含 $include 指令(最多 10 层深度):

json5
// clients/mueller.json5
{
  agents: { $include: "./mueller/agents.json5" },
  broadcast: { $include: "./mueller/broadcast.json5" },
}

Path resolution

  • 相对路径:相对于包含它的文件解析
  • Absolute paths: Used as-is
  • 父目录../ 引用按预期工作
json5
{ "$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

示例:多客户(合规隔离)配置

json5
// ~/.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" } },
}
json5
// ~/.openclaw/clients/mueller/agents.json5
[
  { id: "mueller-transcribe", workspace: "~/clients/mueller/transcribe" },
  { id: "mueller-docs", workspace: "~/clients/mueller/docs" },
]
json5
// ~/.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 时生效(同样遵循“不覆盖”规则):

json5
{
  env: {
    OPENROUTER_API_KEY: "sk-or-...",
    vars: {
      GROQ_API_KEY: "gsk-...",
    },
  },
}

完整的优先级与来源请见 /environment

env.shellEnv(可选)

一个可选的便捷功能:启用后,当预期的 key 都还未设置时,OpenClaw 会运行你的登录 shell,并只导入缺失的预期 key(从不覆盖已存在值)。 这相当于“source”了你的 shell profile。

json5
{
  env: {
    shellEnv: {
      enabled: true,
      timeoutMs: 15000,
    },
  },
}

对应的环境变量:

  • OPENCLAW_LOAD_SHELL_ENV=1
  • OPENCLAW_SHELL_ENV_TIMEOUT_MS=15000

配置中的环境变量替换

你可以在任意配置字符串值中直接使用 ${VAR_NAME} 语法引用环境变量。 变量会在配置加载时、校验之前进行替换。

json5
{
  models: {
    providers: {
      "vercel-gateway": {
        apiKey: "${VERCEL_GATEWAY_API_KEY}",
      },
    },
  },
  gateway: {
    auth: {
      token: "${OPENCLAW_GATEWAY_TOKEN}",
    },
  },
}

规则:

  • 只匹配全大写的环境变量名:[A-Z_][A-Z0-9_]*
  • 缺失或为空的环境变量会在配置加载时抛错
  • $${VAR} 进行转义以输出字面量 ${VAR}
  • 可与 $include 一起使用(被包含文件也会进行替换)

内联替换:

json5
{
  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

另见:/concepts/oauth

旧版 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 轮换顺序。

json5
{
  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.ackReaction from the active agent’s identity.emoji (falls back to 👀)
  • agents.list[].groupChat.mentionPatterns from the agent’s identity.name/identity.emoji (so “@Samantha” works in groups across Telegram/Slack/Discord/Google Chat/iMessage/WhatsApp)
  • identity.avatar accepts 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) URL
  • data: URI
json5
{
  agents: {
    list: [
      {
        id: "main",
        identity: {
          name: "Samantha",
          theme: "helpful sloth",
          emoji: "🦥",
          avatar: "avatars/samantha.png",
        },
      },
    ],
  },
}

wizard

Metadata written by CLI wizards (onboard, configure, doctor).

json5
{
  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.file to /tmp/openclaw/openclaw.log.
  • Console output can be tuned separately via:
    • logging.consoleLevel (defaults to info, bumps to debug when --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)
json5
{
  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 in channels.whatsapp.allowFrom (or paired allow store)
  • "open": allow all inbound DMs (requires channels.whatsapp.allowFrom to 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 whatsapp
  • openclaw 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.

json5
{
  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.

json5
{
  channels: {
    whatsapp: { sendReadReceipts: false },
  },
}

channels.whatsapp.accounts (multi-account)

Run multiple WhatsApp accounts in one gateway:

json5
{
  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 default if present; otherwise the first configured account id (sorted).
  • The legacy single-account Baileys auth dir is migrated by openclaw doctor into whatsapp/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):

json5
{
  channels: {
    telegram: {
      accounts: {
        default: {
          name: "Primary bot",
          botToken: "123456:ABC...",
        },
        alerts: {
          name: "Alerts bot",
          botToken: "987654:XYZ...",
        },
      },
    },
  },
}

Notes:

  • default is used when accountId is 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.accountId to 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).
json5
{
  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:

json5
{
  channels: {
    telegram: {
      dmHistoryLimit: 30, // limit DM sessions to 30 user turns
      dms: {
        "123456789": { historyLimit: 50 }, // per-user override (user ID)
      },
    },
  },
}

Resolution order:

  1. Per-DM override: channels.<provider>.dms[userId].historyLimit
  2. Provider default: channels.<provider>.dmHistoryLimit
  3. No limit (all history retained)

Supported providers: telegram, whatsapp, discord, slack, signal, imessage, msteams.

Per-agent override (takes precedence when set, even []):

json5
{
  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):

json5
{
  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:

json5
{
  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.groupPolicy sets the default when a provider’s groupPolicy is unset.
  • WhatsApp/Telegram/Signal/iMessage/Microsoft Teams use groupAllowFrom (fallback: explicit allowFrom).
  • 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 by channels.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> (for main, falls back to agents.defaults.workspace).
    • agentDir: default ~/.openclaw/agents/<agentId>/agent.
    • model: per-agent default model, overrides agents.defaults.model for that agent.
      • string form: "provider/model", overrides only agents.defaults.model.primary
      • object form: { primary, fallbacks } (fallbacks override agents.defaults.model.fallbacks; [] disables global fallbacks for that agent)
    • identity: per-agent name/theme/emoji (used for mention patterns + ack reactions).
    • groupChat: per-agent mention-gating (mentionPatterns).
    • sandbox: per-agent sandbox config (overrides agents.defaults.sandbox).
      • mode: "off" | "non-main" | "all"
      • workspaceAccess: "none" | "ro" | "rw"
      • scope: "session" | "agent" | "shared"
      • workspaceRoot: custom sandbox workspace root
      • docker: per-agent docker overrides (e.g. image, network, env, setupCommand, limits; ignored when scope: "shared")
      • browser: per-agent sandboxed browser overrides (ignored when scope: "shared")
      • prune: per-agent sandbox pruning overrides (ignored when scope: "shared")
    • subagents: per-agent sub-agent defaults.
      • allowAgents: allowlist of agent ids for sessions_spawn from 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 names
      • deny: array of denied tool names (deny wins)
  • agents.defaults: shared agent defaults (model, workspace, sandbox, etc.).
  • bindings[]: routes inbound messages to an agentId.
    • 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:

  1. match.peer
  2. match.guildId
  3. match.teamId
  4. match.accountId (exact, no peer/guild/team)
  5. match.accountId: "*" (channel-wide, no peer/guild/team)
  6. 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):

json5
{
  agents: {
    list: [
      {
        id: "personal",
        workspace: "~/.openclaw/workspace-personal",
        sandbox: { mode: "off" },
      },
    ],
  },
}

Read-only tools + read-only workspace:

json5
{
  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):

json5
{
  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:

json5
{
  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:

json5
{
  tools: {
    agentToAgent: {
      enabled: false,
      allow: ["home", "work"],
    },
  },
}

messages.queue

Controls how inbound messages behave when an agent run is already active.

json5
{
  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)。

json5
{
  messages: {
    inbound: {
      debounceMs: 2000, // 0 disables
      byChannel: {
        whatsapp: 5000,
        slack: 1500,
        discord: 1500,
      },
    },
  },
}

备注:

  • 去抖只会批量处理纯文本消息;媒体/附件会立即 flush(立刻触发一次 turn)。
  • 控制命令(例如 /queue/new)会绕过去抖,因此始终保持为独立消息。

commands (chat command handling)

控制在各个连接器(connectors)上如何启用聊天命令。

json5
{
  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.nativechannels.telegram.commands.nativechannels.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

json5
{
  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

json5
{
  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 stream streams 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.

json5
{
  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.enabledfalse)。为 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.

json5
{
  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_ACCOUNT or GOOGLE_CHAT_SERVICE_ACCOUNT_FILE.
  • audienceType + audience must match the Chat app’s webhook auth config.
  • Use spaces/<spaceId> or users/<userId|email> when setting delivery targets.

channels.slack (socket mode)

Slack runs in Socket Mode and requires both a bot token and app token:

json5
{
  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.allowBotschannels.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默认说明
reactionsenabled点赞 + 列出 reactions
messagesenabled读/发/编辑/删除
pinsenabledPin/unpin/list
memberInfoenabled成员信息
emojiListenabled自定义 emoji 列表

channels.mattermost (bot token)

Mattermost 以插件形式提供,不包含在核心安装中。 请先安装:openclaw plugins install @openclaw/mattermost(或在 git checkout 中使用 ./extensions/mattermost)。

Mattermost 需要 bot token 以及你的服务器 base URL:

json5
{
  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.enabledfalse)。

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):

json5
{
  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 from channels.signal.reactionAllowlist on all messages (empty list disables).

channels.imessage (imsg CLI)

OpenClaw 会启动 imsg rpc(通过 stdio 的 JSON-RPC)。不需要 daemon 或端口。

json5
{
  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.cliPath can point to a wrapper script (e.g. ssh to another Mac that runs imsg rpc); use SSH keys to avoid password prompts.
  • For remote SSH wrappers, set channels.imessage.remoteHost to fetch attachments via SCP when includeAttachments is enabled.

Example wrapper:

bash
#!/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.

json5
{
  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.

json5
{
  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.

json5
{
  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.

json5
{
  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.

json5
{
  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).

json5
{
  agents: { defaults: { timeFormat: "auto" } }, // auto | 12 | 24
}

messages

Controls inbound/outbound prefixes and optional ack reactions. See Messages for queueing, sessions, and streaming context.

json5
{
  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:

VariableDescriptionExample
{model}Short model nameclaude-opus-4-5, gpt-4o
{modelFull}Full model identifieranthropic/claude-opus-4-5
{provider}Provider nameanthropic, openai
{thinkingLevel}Current thinking levelhigh, 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.

json5
{
  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 mentioned
  • group-all: all group/room messages
  • direct: direct messages only
  • all: 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.

json5
{
  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.auto controls auto‑TTS (off, always, inbound, tagged).
  • /tts off|always|inbound|tagged sets the per‑session auto mode (overrides config).
  • messages.tts.enabled is legacy; doctor migrates it to messages.tts.auto.
  • prefsPath stores local overrides (provider/limit/summarize).
  • maxTextLength is a hard cap for TTS input; summaries are truncated to fit.
  • summaryModel overrides agents.defaults.model.primary for auto-summary.
    • Accepts provider/model or an alias from agents.defaults.models.
  • modelOverrides enables model-driven overrides like [[tts:...]] tags (on by default).
  • /tts limit and /tts summary control per-user summarization settings.
  • apiKey values fall back to ELEVENLABS_API_KEY/XI_API_KEY and OPENAI_API_KEY.
  • elevenlabs.baseUrl overrides the ElevenLabs API base URL.
  • elevenlabs.voiceSettings supports stability/similarityBoost/style (0..1), useSpeakerBoost, and speed (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").

json5
{
  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:

json5
{
  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-5
  • sonnet -> anthropic/claude-sonnet-4-5
  • gpt -> openai/gpt-5.2
  • gpt-mini -> openai/gpt-5-mini
  • gemini -> google/gemini-3-pro-preview
  • gemini-flash -> google/gemini-3-flash-preview

如果你自己配置了同名 alias(大小写不敏感),则以你的值为准(默认值不会覆盖)。

示例:primary 使用 Opus 4.5,fallback 使用 MiniMax M2.1(托管 MiniMax):

json5
{
  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 sessionArg is 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:

json5
{
  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",
        },
      },
    },
  },
}
json5
{
  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: …]")
  • Hard-clear(硬清理):把整个 tool result 替换为占位符。
    • Before: toolResult("…very long output…")
    • After: toolResult("[Old tool result content cleared]")

备注 / 当前限制:

  • 目前包含图片 blocks 的 tool results 会被跳过(永不 trim/clear)。
  • 估算的 “context ratio” 基于字符数(近似值),而不是精确 tokens。
  • 如果 session 里还没有至少 keepLastAssistants 条 assistant 消息,则跳过裁剪。
  • aggressive 模式下会忽略 hardClear.enabled(符合条件的 tool results 总会被替换为 hardClear.placeholder)。

Default (adaptive):

json5
{
  agents: { defaults: { contextPruning: { mode: "adaptive" } } },
}

To disable:

json5
{
  agents: { defaults: { contextPruning: { mode: "off" } } },
}

Defaults (when mode is "adaptive" or "aggressive"):

  • keepLastAssistants: 3
  • softTrimRatio: 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):

json5
{
  agents: { defaults: { contextPruning: { mode: "aggressive" } } },
}

Example (adaptive tuned):

json5
{
  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: true
  • memoryFlush.softThresholdTokens: 4000
  • memoryFlush.prompt / memoryFlush.systemPrompt: built-in defaults with NO_REPLY
  • Note: memory flush is skipped when the session workspace is read-only (agents.defaults.sandbox.workspaceAccess: "ro" or "none").

Example (tuned):

json5
{
  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: true to 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 inherits minChars from blockStreamingChunk with maxChars capped to the channel text limit. Signal/Slack/Discord/Google Chat default to minChars: 1500 unless 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 (use minMs/maxMs). Per-agent override: agents.list[].humanDelay. Example:
    json5
    {
      agents: { defaults: { humanDelay: { mode: "natural" } } },
    }
    See /concepts/streaming for behavior + chunking details.

Typing indicators:

  • agents.defaults.typingMode: "never" | "instant" | "thinking" | "message". Defaults to instant for direct chats / mentions and message for 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. Set 0m to disable.
  • model: optional override model for heartbeat runs (provider/model).
  • includeReasoning: when true, heartbeats will also deliver the separate Reasoning: 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 a Read HEARTBEAT.md line if you still want the file read.
  • ackMaxChars: max chars allowed after HEARTBEAT_OK before delivery (default: 300).

Per-agent heartbeats:

  • Set agents.list[].heartbeat to 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 experimental apply_patch (OpenAI/OpenAI Codex only; default false)
  • applyPatch.allowModels: optional allowlist of model ids (e.g. gpt-5.2 or openai/gpt-5.2) Note: applyPatch is only under tools.exec.

tools.web configures web search + fetch tools:

  • tools.web.search.enabled (default: true when key is present)
  • tools.web.search.apiKey (recommended: set via openclaw configure --section web, or use BRAVE_API_KEY env 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 to FIRECRAWL_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 a maxChars hint 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) with match.channel, match.chatType, or match.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 to gpt-4o-mini-transcribe/whisper-large-v3-turbo for audio providers, and gemini-3-flash-preview for 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, language can be overridden per 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:

json5
{
  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; set 0 to 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_status only
  • coding: group:fs, group:runtime, group:sessions, group:memory, image
  • messaging: group:messaging, sessions_list, sessions_history, sessions_send, session_status
  • full: no restriction (same as unset)

Per-agent override: agents.list[].tools.profile.

Example (messaging-only by default, allow Slack + Discord tools too):

json5
{
  tools: {
    profile: "messaging",
    allow: ["slack", "discord"],
  },
}

Example (coding profile, but deny exec/process everywhere):

json5
{
  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):

json5
{
  tools: {
    profile: "coding",
    byProvider: {
      "google-antigravity": { profile: "minimal" },
    },
  },
}

Example (provider/model-specific allowlist):

json5
{
  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):

json5
{
  tools: { deny: ["browser", "canvas"] },
}

Tool groups (shorthands) work in global and per-agent tool policies:

  • group:runtime: exec, bash, process
  • group:fs: read, write, edit, apply_patch
  • group:sessions: sessions_list, sessions_history, sessions_send, sessions_spawn, session_status
  • group:memory: memory_search, memory_get
  • group:web: web_search, web_fetch
  • group:ui: browser, canvas
  • group:automation: cron, gateway
  • group:messaging: message
  • group:nodes: nodes
  • group: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 numbers
    • telegram: chat ids or usernames
    • discord: user ids or usernames (falls back to channels.discord.dm.allowFrom if omitted)
    • signal: E.164 numbers
    • imessage: handles/chat ids
    • webchat: session ids or usernames

Example:

json5
{
  tools: {
    elevated: {
      enabled: true,
      allowFrom: {
        whatsapp: ["+15555550123"],
        discord: ["steipete", "1234567890123"],
      },
    },
  },
}

Per-agent override (further restrict):

json5
{
  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:仅允许 execprocessreadwriteeditapply_patchsessions_listsessions_historysessions_sendsessions_spawnsession_status(deny 优先)
    • 通过 tools.sandbox.tools 配置;可用 agents.list[].tools.sandbox.tools 做 per-agent 覆盖
    • sandbox policy 支持 tool group 缩写:group:runtimegroup:fsgroup:sessionsgroup:memory(见 Sandbox vs Tool Policy vs Elevated
  • 可选 sandboxed browser(Chromium + CDP,noVNC observer)
  • 加固开关:networkuserpidsLimitmemorycpusulimitsseccompProfileapparmorProfile

警告:scope: "shared" 表示共享 container 与共享 workspace,没有 跨 session 隔离。若需要 per-session 隔离,请使用 scope: "session"

Legacy:仍支持 perSessiontruescope: "session"falsescope: "shared")。

setupCommand 会在 container 创建完成后只运行 一次(在容器内通过 sh -lc 执行)。 如果要安装包,请确保允许网络出站、root 文件系统可写,并且使用 root 用户。

json5
{
  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 镜像:

bash
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 镜像:

bash
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)选择模型。

json5
{
  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.
json5
{
  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.

json5
{
  agents: {
    defaults: {
      model: { primary: "zai/glm-4.7" },
      models: { "zai/glm-4.7": {} },
    },
  },
}

Notes:

  • z.ai/* and z-ai/* are accepted aliases and normalize to zai/*.
  • If ZAI_API_KEY is missing, requests to zai/* 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 endpoint https://api.z.ai/api/coding/paas/v4. The built-in zai provider uses the Coding endpoint. If you need the general endpoint, define a custom provider in models.providers with 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:

json5
{
  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_KEY in the environment or use openclaw onboard --auth-choice moonshot-api-key.
  • Model ref: moonshot/kimi-k2.5.
  • Use https://api.moonshot.cn/v1 if you need the China endpoint.

Kimi Coding

Use Moonshot AI's Kimi Coding endpoint (Anthropic-compatible, built-in provider):

json5
{
  env: { KIMI_API_KEY: "sk-..." },
  agents: {
    defaults: {
      model: { primary: "kimi-coding/k2p5" },
      models: { "kimi-coding/k2p5": { alias: "Kimi K2.5" } },
    },
  },
}

Notes:

  • Set KIMI_API_KEY in the environment or use openclaw onboard --auth-choice kimi-code-api-key.
  • Model ref: kimi-coding/k2p5.

Synthetic (Anthropic-compatible)

Use Synthetic's Anthropic-compatible endpoint:

json5
{
  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_KEY or use openclaw onboard --auth-choice synthetic-api-key.
  • Model ref: synthetic/hf:MiniMaxAI/MiniMax-M2.1.
  • Base URL should omit /v1 because the Anthropic client appends it.

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:

json5
{
  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_KEY environment variable or use openclaw onboard --auth-choice minimax-api.
  • Available model: MiniMax-M2.1 (default).
  • Update pricing in models.json if you need exact cost tracking.

Cerebras (GLM 4.6 / 4.7)

Use Cerebras via their OpenAI-compatible endpoint:

json5
{
  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.7 for Cerebras; use zai/glm-4.7 for Z.AI direct.
  • Set CEREBRAS_API_KEY in the environment or config.

Notes:

  • Supported APIs: openai-completions, openai-responses, anthropic-messages, google-generative-ai
  • Use authHeader: true + headers for custom auth needs.
  • Override the agent config root with OPENCLAW_AGENT_DIR (or PI_CODING_AGENT_DIR) if you want models.json stored elsewhere (default: ~/.openclaw/agents/main/agent).

session

Controls session scoping, reset policy, reset triggers, and where the session store is written.

json5
{
  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 changing agentId.
    • Sandbox note: agents.defaults.sandbox.mode: "non-main" uses this key to detect the main session. Any session key that does not match mainKey (groups/channels) is sandboxed.
  • 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 using per-peer, per-channel-peer, or per-account-channel-peer.
    • Example: alice: ["telegram:123456789", "discord:987654321012345678"].
  • reset: primary reset policy. Defaults to daily resets at 4:00 AM local time on the gateway host.
    • mode: daily or idle (default: daily when reset is 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 for dm, group, and thread.
    • If you only set legacy session.idleMinutes without any reset/resetByType, OpenClaw stays in idle-only mode for backward compatibility.
  • 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: allow or deny fallback when no rule matches.
  • sendPolicy.rules[]: match by channel, chatType (direct|group|room), or keyPrefix (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: set false to 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-proGEMINI_API_KEY).

Example:

json5
{
  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: set false to disable.
    • config: plugin-specific config object (validated by the plugin if provided).

Example:

json5
{
  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 (set false to disable act:evaluate and wait --fn)
  • control service: loopback only (port derived from gateway.port, default 18791)
  • 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.
json5
{
  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 模式气泡的色调)。

如果未设置,客户端会回退到较柔和的浅蓝色。

json5
{
  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 共用单一端口)
json5
{
  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 设置握手认证方式(tokenpassword)。未设置时默认认为是 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-forx-forwarded-protox-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 远程传输方式(默认 sshdirect 对应 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.modegateway.remote.url 变化时会实时切换模式。
  • gateway.mode 未设置但设置了 gateway.remote.url,macOS app 会将其视为 remote mode。
  • 当你在 macOS app 中切换连接模式时,它会把 gateway.mode(以及 remote 模式下的 gateway.remote.url + gateway.remote.transport)写回配置文件。
json5
{
  gateway: {
    mode: "remote",
    remote: {
      url: "ws://gateway.tailnet:18789",
      token: "your-token",
      password: "your-password",
    },
  },
}

Direct transport 示例(macOS app):

json5
{
  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:禁用热重载。
json5
{
  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)
  • telegramdiscordsignalimessage(重启通道)
  • agentmodelsroutingmessagessessionwhatsapploggingskillsuitalkidentitywizard(动态读取)

需要完整重启 Gateway:

  • gateway(port/bind/auth/control UI/tailscale)
  • bridge(legacy)
  • discovery
  • canvasHost
  • plugins
  • 任何未知/不支持的配置路径(为安全起见默认重启)

多实例隔离

要在同一台主机上运行多个 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

示例:

bash
OPENCLAW_CONFIG_PATH=~/.openclaw/a.json \
OPENCLAW_STATE_DIR=~/.openclaw-a \
openclaw gateway --port 19001

hooks(Gateway Webhooks)

在 Gateway HTTP server 上启用一个简单的 HTTP webhook 端点。

默认值:

  • enabled: false
  • path: /hooks
  • maxBodyBytes: 262144(256 KB)
json5
{
  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:&#123;&#123;messages[0].id&#125;&#125;",
        messageTemplate: "From: &#123;&#123;messages[0].from&#125;&#125;\nSubject: &#123;&#123;messages[0].subject&#125;&#125;\n&#123;&#123;messages[0].snippet&#125;&#125;",
        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/gmailgmail)。
  • match.source 匹配 payload 字段(例如 { source: "gmail" }),这样你可以使用通用的 /hooks/ingest 路径。
  • &#123;&#123;messages[0].subject&#125;&#125; 这类模板从 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 使用):

json5
{
  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 }
json5
{
  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

绑定模式:

  • lan0.0.0.0(任意网卡可达,包括 LAN/Wi‑Fi 与 Tailscale)
  • tailnet:仅绑定到机器的 Tailscale IP(推荐用于 Vienna ⇄ London)
  • loopback127.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=1bridgeTlsSha256,用于让节点 pin 证书。 手动连接在未存储 fingerprint 时使用 trust-on-first-use。 自动生成证书要求 openssl 在 PATH 中;若生成失败,bridge 将无法启动。

json5
{
  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 + sshPort
  • full:在 TXT 记录中包含 cliPath + sshPort
  • off:完全禁用 mDNS 广播
  • Hostname:默认 openclaw(会公布 openclaw.local)。可用 OPENCLAW_MDNS_HOSTNAME 覆盖。
json5
{
  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 主机):

bash
openclaw dns setup --apply
json5
{
  discovery: { wideArea: { enabled: true } },
}

模板变量

模板占位符会在 tools.media.*.models[].argstools.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

json5
{
  cron: {
    enabled: true,
    maxConcurrentRuns: 2,
  },
}

下一页:Agent Runtime 🦞