Skip to main content
Agent-scoped configuration keys under agents.*, multiAgent.*, session.*, messages.*, and talk.*. For channels, tools, gateway runtime, and other top-level keys, see Configuration reference.

Agent defaults

agents.defaults.workspace

Default: ~/.openclaw/workspace.
{
  agents: { defaults: { workspace: "~/.openclaw/workspace" } },
}

agents.defaults.repoRoot

Optional repository root shown in the system prompt’s Runtime line. If unset, OpenClaw auto-detects by walking upward from the workspace.
{
  agents: { defaults: { repoRoot: "~/Projects/openclaw" } },
}

agents.defaults.skills

Optional default skill allowlist for agents that do not set agents.list[].skills.
{
  agents: {
    defaults: { skills: ["github", "weather"] },
    list: [
      { id: "writer" }, // inherits github, weather
      { id: "docs", skills: ["docs-search"] }, // replaces defaults
      { id: "locked-down", skills: [] }, // no skills
    ],
  },
}
  • Omit agents.defaults.skills for unrestricted skills by default.
  • Omit agents.list[].skills to inherit the defaults.
  • Set agents.list[].skills: [] for no skills.
  • A non-empty agents.list[].skills list is the final set for that agent; it does not merge with defaults.

agents.defaults.skipBootstrap

Disables automatic creation of workspace bootstrap files (AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, USER.md, HEARTBEAT.md, BOOTSTRAP.md).
{
  agents: { defaults: { skipBootstrap: true } },
}

agents.defaults.contextInjection

Controls when workspace bootstrap files are injected into the system prompt. Default: "always".
  • "continuation-skip": safe continuation turns (after a completed assistant response) skip workspace bootstrap re-injection, reducing prompt size. Heartbeat runs and post-compaction retries still rebuild context.
{
  agents: { defaults: { contextInjection: "continuation-skip" } },
}

agents.defaults.bootstrapMaxChars

Max characters per workspace bootstrap file before truncation. Default: 12000.
{
  agents: { defaults: { bootstrapMaxChars: 12000 } },
}

agents.defaults.bootstrapTotalMaxChars

Max total characters injected across all workspace bootstrap files. Default: 60000.
{
  agents: { defaults: { bootstrapTotalMaxChars: 60000 } },
}

agents.defaults.bootstrapPromptTruncationWarning

Controls agent-visible warning text when bootstrap context is truncated. Default: "once".
  • "off": never inject warning text into the system prompt.
  • "once": inject warning once per unique truncation signature (recommended).
  • "always": inject warning on every run when truncation exists.
{
  agents: { defaults: { bootstrapPromptTruncationWarning: "once" } }, // off | once | always
}

Context budget ownership map

OpenClaw has multiple high-volume prompt/context budgets, and they are intentionally split by subsystem instead of all flowing through one generic knob.
  • agents.defaults.bootstrapMaxChars / agents.defaults.bootstrapTotalMaxChars: normal workspace bootstrap injection.
  • agents.defaults.startupContext.*: one-shot /new and /reset startup prelude, including recent daily memory/*.md files.
  • skills.limits.*: the compact skills list injected into the system prompt.
  • agents.defaults.contextLimits.*: bounded runtime excerpts and injected runtime-owned blocks.
  • memory.qmd.limits.*: indexed memory-search snippet and injection sizing.
Use the matching per-agent override only when one agent needs a different budget:
  • agents.list[].skillsLimits.maxSkillsPromptChars
  • agents.list[].contextLimits.*

agents.defaults.startupContext

Controls the first-turn startup prelude injected on bare /new and /reset runs.
{
  agents: {
    defaults: {
      startupContext: {
        enabled: true,
        applyOn: ["new", "reset"],
        dailyMemoryDays: 2,
        maxFileBytes: 16384,
        maxFileChars: 1200,
        maxTotalChars: 2800,
      },
    },
  },
}

agents.defaults.contextLimits

Shared defaults for bounded runtime context surfaces.
{
  agents: {
    defaults: {
      contextLimits: {
        memoryGetMaxChars: 12000,
        memoryGetDefaultLines: 120,
        toolResultMaxChars: 16000,
        postCompactionMaxChars: 1800,
      },
    },
  },
}
  • memoryGetMaxChars: default memory_get excerpt cap before truncation metadata and continuation notice are added.
  • memoryGetDefaultLines: default memory_get line window when lines is omitted.
  • toolResultMaxChars: live tool-result cap used for persisted results and overflow recovery.
  • postCompactionMaxChars: AGENTS.md excerpt cap used during post-compaction refresh injection.

agents.list[].contextLimits

Per-agent override for the shared contextLimits knobs. Omitted fields inherit from agents.defaults.contextLimits.
{
  agents: {
    defaults: {
      contextLimits: {
        memoryGetMaxChars: 12000,
        toolResultMaxChars: 16000,
      },
    },
    list: [
      {
        id: "tiny-local",
        contextLimits: {
          memoryGetMaxChars: 6000,
          toolResultMaxChars: 8000,
        },
      },
    ],
  },
}

skills.limits.maxSkillsPromptChars

Global cap for the compact skills list injected into the system prompt. This does not affect reading SKILL.md files on demand.
{
  skills: {
    limits: {
      maxSkillsPromptChars: 18000,
    },
  },
}

agents.list[].skillsLimits.maxSkillsPromptChars

Per-agent override for the skills prompt budget.
{
  agents: {
    list: [
      {
        id: "tiny-local",
        skillsLimits: {
          maxSkillsPromptChars: 6000,
        },
      },
    ],
  },
}

agents.defaults.imageMaxDimensionPx

Max pixel size for the longest image side in transcript/tool image blocks before provider calls. Default: 1200. Lower values usually reduce vision-token usage and request payload size for screenshot-heavy runs. Higher values preserve more visual detail.
{
  agents: { defaults: { imageMaxDimensionPx: 1200 } },
}

agents.defaults.userTimezone

Timezone for system prompt context (not message timestamps). Falls back to host timezone.
{
  agents: { defaults: { userTimezone: "America/Chicago" } },
}

agents.defaults.timeFormat

Time format in system prompt. Default: auto (OS preference).
{
  agents: { defaults: { timeFormat: "auto" } }, // auto | 12 | 24
}

agents.defaults.model

{
  agents: {
    defaults: {
      models: {
        "anthropic/claude-opus-4-6": { alias: "opus" },
        "minimax/MiniMax-M2.7": { alias: "minimax" },
      },
      model: {
        primary: "anthropic/claude-opus-4-6",
        fallbacks: ["minimax/MiniMax-M2.7"],
      },
      imageModel: {
        primary: "openrouter/qwen/qwen-2.5-vl-72b-instruct:free",
        fallbacks: ["openrouter/google/gemini-2.0-flash-vision:free"],
      },
      imageGenerationModel: {
        primary: "openai/gpt-image-2",
        fallbacks: ["google/gemini-3.1-flash-image-preview"],
      },
      videoGenerationModel: {
        primary: "qwen/wan2.6-t2v",
        fallbacks: ["qwen/wan2.6-i2v"],
      },
      pdfModel: {
        primary: "anthropic/claude-opus-4-6",
        fallbacks: ["openai/gpt-5.4-mini"],
      },
      params: { cacheRetention: "long" }, // global default provider params
      embeddedHarness: {
        runtime: "auto", // auto | pi | registered harness id, e.g. codex
        fallback: "pi", // pi | none
      },
      pdfMaxBytesMb: 10,
      pdfMaxPages: 20,
      thinkingDefault: "low",
      verboseDefault: "off",
      elevatedDefault: "on",
      timeoutSeconds: 600,
      mediaMaxMb: 5,
      contextTokens: 200000,
      maxConcurrent: 3,
    },
  },
}
  • model: accepts either a string ("provider/model") or an object ({ primary, fallbacks }).
    • String form sets only the primary model.
    • Object form sets primary plus ordered failover models.
  • imageModel: accepts either a string ("provider/model") or an object ({ primary, fallbacks }).
    • Used by the image tool path as its vision-model config.
    • Also used as fallback routing when the selected/default model cannot accept image input.
  • imageGenerationModel: accepts either a string ("provider/model") or an object ({ primary, fallbacks }).
    • Used by the shared image-generation capability and any future tool/plugin surface that generates images.
    • Typical values: google/gemini-3.1-flash-image-preview for native Gemini image generation, fal/fal-ai/flux/dev for fal, or openai/gpt-image-2 for OpenAI Images.
    • If you select a provider/model directly, configure matching provider auth too (for example GEMINI_API_KEY or GOOGLE_API_KEY for google/*, OPENAI_API_KEY or OpenAI Codex OAuth for openai/gpt-image-2, FAL_KEY for fal/*).
    • If omitted, image_generate can still infer an auth-backed provider default. It tries the current default provider first, then the remaining registered image-generation providers in provider-id order.
  • musicGenerationModel: accepts either a string ("provider/model") or an object ({ primary, fallbacks }).
    • Used by the shared music-generation capability and the built-in music_generate tool.
    • Typical values: google/lyria-3-clip-preview, google/lyria-3-pro-preview, or minimax/music-2.5+.
    • If omitted, music_generate can still infer an auth-backed provider default. It tries the current default provider first, then the remaining registered music-generation providers in provider-id order.
    • If you select a provider/model directly, configure the matching provider auth/API key too.
  • videoGenerationModel: accepts either a string ("provider/model") or an object ({ primary, fallbacks }).
    • Used by the shared video-generation capability and the built-in video_generate tool.
    • Typical values: qwen/wan2.6-t2v, qwen/wan2.6-i2v, qwen/wan2.6-r2v, qwen/wan2.6-r2v-flash, or qwen/wan2.7-r2v.
    • If omitted, video_generate can still infer an auth-backed provider default. It tries the current default provider first, then the remaining registered video-generation providers in provider-id order.
    • If you select a provider/model directly, configure the matching provider auth/API key too.
    • The bundled Qwen video-generation provider supports up to 1 output video, 1 input image, 4 input videos, 10 seconds duration, and provider-level size, aspectRatio, resolution, audio, and watermark options.
  • pdfModel: accepts either a string ("provider/model") or an object ({ primary, fallbacks }).
    • Used by the pdf tool for model routing.
    • If omitted, the PDF tool falls back to imageModel, then to the resolved session/default model.
  • pdfMaxBytesMb: default PDF size limit for the pdf tool when maxBytesMb is not passed at call time.
  • pdfMaxPages: default maximum pages considered by extraction fallback mode in the pdf tool.
  • verboseDefault: default verbose level for agents. Values: "off", "on", "full". Default: "off".
  • elevatedDefault: default elevated-output level for agents. Values: "off", "on", "ask", "full". Default: "on".
  • model.primary: format provider/model (e.g. openai/gpt-5.4 for API-key access or openai-codex/gpt-5.5 for Codex OAuth). If you omit the provider, OpenClaw tries an alias first, then a unique configured-provider match for that exact model id, and only then falls back to the configured default provider (deprecated compatibility behavior, so prefer explicit provider/model). If that provider no longer exposes the configured default model, OpenClaw falls back to the first configured provider/model instead of surfacing a stale removed-provider default.
  • models: the configured model catalog and allowlist for /model. Each entry can include alias (shortcut) and params (provider-specific, for example temperature, maxTokens, cacheRetention, context1m, responsesServerCompaction, responsesCompactThreshold).
    • Safe edits: use openclaw config set agents.defaults.models '<json>' --strict-json --merge to add entries. config set refuses replacements that would remove existing allowlist entries unless you pass --replace.
    • Provider-scoped configure/onboarding flows merge selected provider models into this map and preserve unrelated providers already configured.
    • For direct OpenAI Responses models, server-side compaction is enabled automatically. Use params.responsesServerCompaction: false to stop injecting context_management, or params.responsesCompactThreshold to override the threshold. See OpenAI server-side compaction.
  • params: global default provider parameters applied to all models. Set at agents.defaults.params (e.g. { cacheRetention: "long" }).
  • params merge precedence (config): agents.defaults.params (global base) is overridden by agents.defaults.models["provider/model"].params (per-model), then agents.list[].params (matching agent id) overrides by key. See Prompt Caching for details.
  • embeddedHarness: default low-level embedded agent runtime policy. Use runtime: "auto" to let registered plugin harnesses claim supported models, runtime: "pi" to force the built-in PI harness, or a registered harness id such as runtime: "codex". Set fallback: "none" to disable automatic PI fallback.
  • Config writers that mutate these fields (for example /models set, /models set-image, and fallback add/remove commands) save canonical object form and preserve existing fallback lists when possible.
  • maxConcurrent: max parallel agent runs across sessions (each session still serialized). Default: 4.

agents.defaults.embeddedHarness

embeddedHarness controls which low-level executor runs embedded agent turns. Most deployments should keep the default { runtime: "auto", fallback: "pi" }. Use it when a trusted plugin provides a native harness, such as the bundled Codex app-server harness.
{
  agents: {
    defaults: {
      model: "openai/gpt-5.5",
      embeddedHarness: {
        runtime: "codex",
        fallback: "none",
      },
    },
  },
}
  • runtime: "auto", "pi", or a registered plugin harness id. The bundled Codex plugin registers codex.
  • fallback: "pi" or "none". "pi" keeps the built-in PI harness as the compatibility fallback when no plugin harness is selected. "none" makes missing or unsupported plugin harness selection fail instead of silently using PI. Selected plugin harness failures always surface directly.
  • Environment overrides: OPENCLAW_AGENT_RUNTIME=<id|auto|pi> overrides runtime; OPENCLAW_AGENT_HARNESS_FALLBACK=none disables PI fallback for that process.
  • For Codex-only deployments, set model: "openai/gpt-5.5", embeddedHarness.runtime: "codex", and embeddedHarness.fallback: "none".
  • Harness choice is pinned per session id after the first embedded run. Config/env changes affect new or reset sessions, not an existing transcript. Legacy sessions with transcript history but no recorded pin are treated as PI-pinned. /status shows non-PI harness ids such as codex next to Fast.
  • This only controls the embedded chat harness. Media generation, vision, PDF, music, video, and TTS still use their provider/model settings.
Built-in alias shorthands (only apply when the model is in agents.defaults.models):
AliasModel
opusanthropic/claude-opus-4-6
sonnetanthropic/claude-sonnet-4-6
gptopenai/gpt-5.4 or configured Codex OAuth GPT-5.5
gpt-miniopenai/gpt-5.4-mini
gpt-nanoopenai/gpt-5.4-nano
geminigoogle/gemini-3.1-pro-preview
gemini-flashgoogle/gemini-3-flash-preview
gemini-flash-litegoogle/gemini-3.1-flash-lite-preview
Your configured aliases always win over defaults. Z.AI GLM-4.x models automatically enable thinking mode unless you set --thinking off or define agents.defaults.models["zai/<model>"].params.thinking yourself. Z.AI models enable tool_stream by default for tool call streaming. Set agents.defaults.models["zai/<model>"].params.tool_stream to false to disable it. Anthropic Claude 4.6 models default to adaptive thinking when no explicit thinking level is set.

agents.defaults.cliBackends

Optional CLI backends for text-only fallback runs (no tool calls). Useful as a backup when API providers fail.
{
  agents: {
    defaults: {
      cliBackends: {
        "codex-cli": {
          command: "/opt/homebrew/bin/codex",
        },
        "my-cli": {
          command: "my-cli",
          args: ["--json"],
          output: "json",
          modelArg: "--model",
          sessionArg: "--session",
          sessionMode: "existing",
          systemPromptArg: "--system",
          systemPromptWhen: "first",
          imageArg: "--image",
          imageMode: "repeat",
        },
      },
    },
  },
}
  • CLI backends are text-first; tools are always disabled.
  • Sessions supported when sessionArg is set.
  • Image pass-through supported when imageArg accepts file paths.

agents.defaults.systemPromptOverride

Replace the entire OpenClaw-assembled system prompt with a fixed string. Set at the default level (agents.defaults.systemPromptOverride) or per agent (agents.list[].systemPromptOverride). Per-agent values take precedence; an empty or whitespace-only value is ignored. Useful for controlled prompt experiments.
{
  agents: {
    defaults: {
      systemPromptOverride: "You are a helpful assistant.",
    },
  },
}

agents.defaults.promptOverlays

Provider-independent prompt overlays applied by model family. GPT-5-family model ids receive the shared behavior contract across providers; personality controls only the friendly interaction-style layer.
{
  agents: {
    defaults: {
      promptOverlays: {
        gpt5: {
          personality: "friendly", // friendly | on | off
        },
      },
    },
  },
}
  • "friendly" (default) and "on" enable the friendly interaction-style layer.
  • "off" disables only the friendly layer; the tagged GPT-5 behavior contract remains enabled.
  • Legacy plugins.entries.openai.config.personality is still read when this shared setting is unset.

agents.defaults.heartbeat

Periodic heartbeat runs.
{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m", // 0m disables
        model: "openai/gpt-5.4-mini",
        includeReasoning: false,
        includeSystemPromptSection: true, // default: true; false omits the Heartbeat section from the system prompt
        lightContext: false, // default: false; true keeps only HEARTBEAT.md from workspace bootstrap files
        isolatedSession: false, // default: false; true runs each heartbeat in a fresh session (no conversation history)
        session: "main",
        to: "+15555550123",
        directPolicy: "allow", // allow (default) | block
        target: "none", // default: none | options: last | whatsapp | telegram | discord | ...
        prompt: "Read HEARTBEAT.md if it exists...",
        ackMaxChars: 300,
        suppressToolErrorWarnings: false,
        timeoutSeconds: 45,
      },
    },
  },
}
  • every: duration string (ms/s/m/h). Default: 30m (API-key auth) or 1h (OAuth auth). Set to 0m to disable.
  • includeSystemPromptSection: when false, omits the Heartbeat section from the system prompt and skips HEARTBEAT.md injection into bootstrap context. Default: true.
  • suppressToolErrorWarnings: when true, suppresses tool error warning payloads during heartbeat runs.
  • timeoutSeconds: maximum time in seconds allowed for a heartbeat agent turn before it is aborted. Leave unset to use agents.defaults.timeoutSeconds.
  • directPolicy: direct/DM delivery policy. allow (default) permits direct-target delivery. block suppresses direct-target delivery and emits reason=dm-blocked.
  • lightContext: when true, heartbeat runs use lightweight bootstrap context and keep only HEARTBEAT.md from workspace bootstrap files.
  • isolatedSession: when true, each heartbeat runs in a fresh session with no prior conversation history. Same isolation pattern as cron sessionTarget: "isolated". Reduces per-heartbeat token cost from ~100K to ~2-5K tokens.
  • Per-agent: set agents.list[].heartbeat. When any agent defines heartbeat, only those agents run heartbeats.
  • Heartbeats run full agent turns — shorter intervals burn more tokens.

agents.defaults.compaction

{
  agents: {
    defaults: {
      compaction: {
        mode: "safeguard", // default | safeguard
        provider: "my-provider", // id of a registered compaction provider plugin (optional)
        timeoutSeconds: 900,
        reserveTokensFloor: 24000,
        identifierPolicy: "strict", // strict | off | custom
        identifierInstructions: "Preserve deployment IDs, ticket IDs, and host:port pairs exactly.", // used when identifierPolicy=custom
        postCompactionSections: ["Session Startup", "Red Lines"], // [] disables reinjection
        model: "openrouter/anthropic/claude-sonnet-4-6", // optional compaction-only model override
        notifyUser: true, // send brief notices when compaction starts and completes (default: false)
        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 the exact silent token NO_REPLY if nothing to store.",
        },
      },
    },
  },
}
  • mode: default or safeguard (chunked summarization for long histories). See Compaction.
  • provider: id of a registered compaction provider plugin. When set, the provider’s summarize() is called instead of built-in LLM summarization. Falls back to built-in on failure. Setting a provider forces mode: "safeguard". See Compaction.
  • timeoutSeconds: maximum seconds allowed for a single compaction operation before OpenClaw aborts it. Default: 900.
  • identifierPolicy: strict (default), off, or custom. strict prepends built-in opaque identifier retention guidance during compaction summarization.
  • identifierInstructions: optional custom identifier-preservation text used when identifierPolicy=custom.
  • postCompactionSections: optional AGENTS.md H2/H3 section names to re-inject after compaction. Defaults to ["Session Startup", "Red Lines"]; set [] to disable reinjection. When unset or explicitly set to that default pair, older Every Session/Safety headings are also accepted as a legacy fallback.
  • model: optional provider/model-id override for compaction summarization only. Use this when the main session should keep one model but compaction summaries should run on another; when unset, compaction uses the session’s primary model.
  • notifyUser: when true, sends brief notices to the user when compaction starts and when it completes (for example, “Compacting context…” and “Compaction complete”). Disabled by default to keep compaction silent.
  • memoryFlush: silent agentic turn before auto-compaction to store durable memories. Skipped when workspace is read-only.

agents.defaults.contextPruning

Prunes old tool results from in-memory context before sending to the LLM. Does not modify session history on disk.
{
  agents: {
    defaults: {
      contextPruning: {
        mode: "cache-ttl", // off | cache-ttl
        ttl: "1h", // duration (ms/s/m/h), default unit: minutes
        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]" },
        tools: { deny: ["browser", "canvas"] },
      },
    },
  },
}
  • mode: "cache-ttl" enables pruning passes.
  • ttl controls how often pruning can run again (after the last cache touch).
  • Pruning soft-trims oversized tool results first, then hard-clears older tool results if needed.
Soft-trim keeps beginning + end and inserts ... in the middle.Hard-clear replaces the entire tool result with the placeholder.Notes:
  • Image blocks are never trimmed/cleared.
  • Ratios are character-based (approximate), not exact token counts.
  • If fewer than keepLastAssistants assistant messages exist, pruning is skipped.
See Session Pruning for behavior details.

Block streaming

{
  agents: {
    defaults: {
      blockStreamingDefault: "off", // on | off
      blockStreamingBreak: "text_end", // text_end | message_end
      blockStreamingChunk: { minChars: 800, maxChars: 1200 },
      blockStreamingCoalesce: { idleMs: 1000 },
      humanDelay: { mode: "natural" }, // off | natural | custom (use minMs/maxMs)
    },
  },
}
  • Non-Telegram channels require explicit *.blockStreaming: true to enable block replies.
  • Channel overrides: channels.<channel>.blockStreamingCoalesce (and per-account variants). Signal/Slack/Discord/Google Chat default minChars: 1500.
  • humanDelay: randomized pause between block replies. natural = 800–2500ms. Per-agent override: agents.list[].humanDelay.
See Streaming for behavior + chunking details.

Typing indicators

{
  agents: {
    defaults: {
      typingMode: "instant", // never | instant | thinking | message
      typingIntervalSeconds: 6,
    },
  },
}
  • Defaults: instant for direct chats/mentions, message for unmentioned group chats.
  • Per-session overrides: session.typingMode, session.typingIntervalSeconds.
See Typing Indicators.

agents.defaults.sandbox

Optional sandboxing for the embedded agent. See Sandboxing for the full guide.
{
  agents: {
    defaults: {
      sandbox: {
        mode: "non-main", // off | non-main | all
        backend: "docker", // docker | ssh | openshell
        scope: "agent", // session | agent | shared
        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",
          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: ["/home/user/source:/source:rw"],
        },
        ssh: {
          target: "user@gateway-host:22",
          command: "ssh",
          workspaceRoot: "/tmp/openclaw-sandboxes",
          strictHostKeyChecking: true,
          updateHostKeys: true,
          identityFile: "~/.ssh/id_ed25519",
          certificateFile: "~/.ssh/id_ed25519-cert.pub",
          knownHostsFile: "~/.ssh/known_hosts",
          // SecretRefs / inline contents also supported:
          // identityData: { source: "env", provider: "default", id: "SSH_IDENTITY" },
          // certificateData: { source: "env", provider: "default", id: "SSH_CERTIFICATE" },
          // knownHostsData: { source: "env", provider: "default", id: "SSH_KNOWN_HOSTS" },
        },
        browser: {
          enabled: false,
          image: "openclaw-sandbox-browser:bookworm-slim",
          network: "openclaw-sandbox-browser",
          cdpPort: 9222,
          cdpSourceRange: "172.21.0.1/32",
          vncPort: 5900,
          noVncPort: 6080,
          headless: false,
          enableNoVnc: true,
          allowHostControl: false,
          autoStart: true,
          autoStartTimeoutMs: 12000,
        },
        prune: {
          idleHours: 24,
          maxAgeDays: 7,
        },
      },
    },
  },
  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"],
      },
    },
  },
}
Backend:
  • docker: local Docker runtime (default)
  • ssh: generic SSH-backed remote runtime
  • openshell: OpenShell runtime
When backend: "openshell" is selected, runtime-specific settings move to plugins.entries.openshell.config.SSH backend config:
  • target: SSH target in user@host[:port] form
  • command: SSH client command (default: ssh)
  • workspaceRoot: absolute remote root used for per-scope workspaces
  • identityFile / certificateFile / knownHostsFile: existing local files passed to OpenSSH
  • identityData / certificateData / knownHostsData: inline contents or SecretRefs that OpenClaw materializes into temp files at runtime
  • strictHostKeyChecking / updateHostKeys: OpenSSH host-key policy knobs
SSH auth precedence:
  • identityData wins over identityFile
  • certificateData wins over certificateFile
  • knownHostsData wins over knownHostsFile
  • SecretRef-backed *Data values are resolved from the active secrets runtime snapshot before the sandbox session starts
SSH backend behavior:
  • seeds the remote workspace once after create or recreate
  • then keeps the remote SSH workspace canonical
  • routes exec, file tools, and media paths over SSH
  • does not sync remote changes back to the host automatically
  • does not support sandbox browser containers
Workspace access:
  • none: per-scope sandbox workspace under ~/.openclaw/sandboxes
  • ro: sandbox workspace at /workspace, agent workspace mounted read-only at /agent
  • rw: agent workspace mounted read/write at /workspace
Scope:
  • session: per-session container + workspace
  • agent: one container + workspace per agent (default)
  • shared: shared container and workspace (no cross-session isolation)
OpenShell plugin config:
{
  plugins: {
    entries: {
      openshell: {
        enabled: true,
        config: {
          mode: "mirror", // mirror | remote
          from: "openclaw",
          remoteWorkspaceDir: "/sandbox",
          remoteAgentWorkspaceDir: "/agent",
          gateway: "lab", // optional
          gatewayEndpoint: "https://lab.example", // optional
          policy: "strict", // optional OpenShell policy id
          providers: ["openai"], // optional
          autoProviders: true,
          timeoutSeconds: 120,
        },
      },
    },
  },
}
OpenShell mode:
  • mirror: seed remote from local before exec, sync back after exec; local workspace stays canonical
  • remote: seed remote once when the sandbox is created, then keep the remote workspace canonical
In remote mode, host-local edits made outside OpenClaw are not synced into the sandbox automatically after the seed step. Transport is SSH into the OpenShell sandbox, but the plugin owns sandbox lifecycle and optional mirror sync.setupCommand runs once after container creation (via sh -lc). Needs network egress, writable root, root user.Containers default to network: "none" — set to "bridge" (or a custom bridge network) if the agent needs outbound access. "host" is blocked. "container:<id>" is blocked by default unless you explicitly set sandbox.docker.dangerouslyAllowContainerNamespaceJoin: true (break-glass).Inbound attachments are staged into media/inbound/* in the active workspace.docker.binds mounts additional host directories; global and per-agent binds are merged.Sandboxed browser (sandbox.browser.enabled): Chromium + CDP in a container. noVNC URL injected into system prompt. Does not require browser.enabled in openclaw.json. noVNC observer access uses VNC auth by default and OpenClaw emits a short-lived token URL (instead of exposing the password in the shared URL).
  • allowHostControl: false (default) blocks sandboxed sessions from targeting the host browser.
  • network defaults to openclaw-sandbox-browser (dedicated bridge network). Set to bridge only when you explicitly want global bridge connectivity.
  • cdpSourceRange optionally restricts CDP ingress at the container edge to a CIDR range (for example 172.21.0.1/32).
  • sandbox.browser.binds mounts additional host directories into the sandbox browser container only. When set (including []), it replaces docker.binds for the browser container.
  • Launch defaults are defined in scripts/sandbox-browser-entrypoint.sh and tuned for container hosts:
    • --remote-debugging-address=127.0.0.1
    • --remote-debugging-port=<derived from OPENCLAW_BROWSER_CDP_PORT>
    • --user-data-dir=${HOME}/.chrome
    • --no-first-run
    • --no-default-browser-check
    • --disable-3d-apis
    • --disable-gpu
    • --disable-software-rasterizer
    • --disable-dev-shm-usage
    • --disable-background-networking
    • --disable-features=TranslateUI
    • --disable-breakpad
    • --disable-crash-reporter
    • --renderer-process-limit=2
    • --no-zygote
    • --metrics-recording-only
    • --disable-extensions (default enabled)
    • --disable-3d-apis, --disable-software-rasterizer, and --disable-gpu are enabled by default and can be disabled with OPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0 if WebGL/3D usage requires it.
    • OPENCLAW_BROWSER_DISABLE_EXTENSIONS=0 re-enables extensions if your workflow depends on them.
    • --renderer-process-limit=2 can be changed with OPENCLAW_BROWSER_RENDERER_PROCESS_LIMIT=<N>; set 0 to use Chromium’s default process limit.
    • plus --no-sandbox and --disable-setuid-sandbox when noSandbox is enabled.
    • Defaults are the container image baseline; use a custom browser image with a custom entrypoint to change container defaults.
Browser sandboxing and sandbox.docker.binds are Docker-only. Build images:
scripts/sandbox-setup.sh           # main sandbox image
scripts/sandbox-browser-setup.sh   # optional browser image

agents.list (per-agent overrides)

{
  agents: {
    list: [
      {
        id: "main",
        default: true,
        name: "Main Agent",
        workspace: "~/.openclaw/workspace",
        agentDir: "~/.openclaw/agents/main/agent",
        model: "anthropic/claude-opus-4-6", // or { primary, fallbacks }
        thinkingDefault: "high", // per-agent thinking level override
        reasoningDefault: "on", // per-agent reasoning visibility override
        fastModeDefault: false, // per-agent fast mode override
        embeddedHarness: { runtime: "auto", fallback: "pi" },
        params: { cacheRetention: "none" }, // overrides matching defaults.models params by key
        skills: ["docs-search"], // replaces agents.defaults.skills when set
        identity: {
          name: "Samantha",
          theme: "helpful sloth",
          emoji: "🦥",
          avatar: "avatars/samantha.png",
        },
        groupChat: { mentionPatterns: ["@openclaw"] },
        sandbox: { mode: "off" },
        runtime: {
          type: "acp",
          acp: {
            agent: "codex",
            backend: "acpx",
            mode: "persistent",
            cwd: "/workspace/openclaw",
          },
        },
        subagents: { allowAgents: ["*"] },
        tools: {
          profile: "coding",
          allow: ["browser"],
          deny: ["canvas"],
          elevated: { enabled: true },
        },
      },
    ],
  },
}
  • id: stable agent id (required).
  • default: when multiple are set, first wins (warning logged). If none set, first list entry is default.
  • model: string form overrides primary only; object form { primary, fallbacks } overrides both ([] disables global fallbacks). Cron jobs that only override primary still inherit default fallbacks unless you set fallbacks: [].
  • params: per-agent stream params merged over the selected model entry in agents.defaults.models. Use this for agent-specific overrides like cacheRetention, temperature, or maxTokens without duplicating the whole model catalog.
  • skills: optional per-agent skill allowlist. If omitted, the agent inherits agents.defaults.skills when set; an explicit list replaces defaults instead of merging, and [] means no skills.
  • thinkingDefault: optional per-agent default thinking level (off | minimal | low | medium | high | xhigh | adaptive | max). Overrides agents.defaults.thinkingDefault for this agent when no per-message or session override is set.
  • reasoningDefault: optional per-agent default reasoning visibility (on | off | stream). Applies when no per-message or session reasoning override is set.
  • fastModeDefault: optional per-agent default for fast mode (true | false). Applies when no per-message or session fast-mode override is set.
  • embeddedHarness: optional per-agent low-level harness policy override. Use { runtime: "codex", fallback: "none" } to make one agent Codex-only while other agents keep the default PI fallback.
  • runtime: optional per-agent runtime descriptor. Use type: "acp" with runtime.acp defaults (agent, backend, mode, cwd) when the agent should default to ACP harness sessions.
  • identity.avatar: workspace-relative path, http(s) URL, or data: URI.
  • identity derives defaults: ackReaction from emoji, mentionPatterns from name/emoji.
  • subagents.allowAgents: allowlist of agent ids for sessions_spawn (["*"] = any; default: same agent only).
  • Sandbox inheritance guard: if the requester session is sandboxed, sessions_spawn rejects targets that would run unsandboxed.
  • subagents.requireAgentId: when true, block sessions_spawn calls that omit agentId (forces explicit profile selection; default: false).

Multi-agent routing

Run multiple isolated agents inside one Gateway. See Multi-Agent.
{
  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" } },
  ],
}

Binding match fields

  • type (optional): route for normal routing (missing type defaults to route), acp for persistent ACP conversation bindings.
  • match.channel (required)
  • match.accountId (optional; * = any account; omitted = default account)
  • match.peer (optional; { kind: direct|group|channel, id })
  • match.guildId / match.teamId (optional; channel-specific)
  • acp (optional; only for type: "acp"): { mode, label, cwd, backend }
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)
  6. Default agent
Within each tier, the first matching bindings entry wins. For type: "acp" entries, OpenClaw resolves by exact conversation identity (match.channel + account + match.peer.id) and does not use the route binding tier order above.

Per-agent access profiles

{
  agents: {
    list: [
      {
        id: "personal",
        workspace: "~/.openclaw/workspace-personal",
        sandbox: { mode: "off" },
      },
    ],
  },
}
{
  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"],
        },
      },
    ],
  },
}
{
  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",
          ],
        },
      },
    ],
  },
}
See Multi-Agent Sandbox & Tools for precedence details.

Session

{
  session: {
    scope: "per-sender",
    dmScope: "main", // main | per-peer | per-channel-peer | per-account-channel-peer
    identityLinks: {
      alice: ["telegram:123456789", "discord:987654321012345678"],
    },
    reset: {
      mode: "daily", // daily | idle
      atHour: 4,
      idleMinutes: 60,
    },
    resetByType: {
      thread: { mode: "daily", atHour: 4 },
      direct: { mode: "idle", idleMinutes: 240 },
      group: { mode: "idle", idleMinutes: 120 },
    },
    resetTriggers: ["/new", "/reset"],
    store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
    parentForkMaxTokens: 100000, // skip parent-thread fork above this token count (0 disables)
    maintenance: {
      mode: "warn", // warn | enforce
      pruneAfter: "30d",
      maxEntries: 500,
      rotateBytes: "10mb",
      resetArchiveRetention: "30d", // duration or false
      maxDiskBytes: "500mb", // optional hard budget
      highWaterBytes: "400mb", // optional cleanup target
    },
    threadBindings: {
      enabled: true,
      idleHours: 24, // default inactivity auto-unfocus in hours (`0` disables)
      maxAgeHours: 0, // default hard max age in hours (`0` disables)
    },
    mainKey: "main", // legacy (runtime always uses "main")
    agentToAgent: { maxPingPongTurns: 5 },
    sendPolicy: {
      rules: [{ action: "deny", match: { channel: "discord", chatType: "group" } }],
      default: "allow",
    },
  },
}
  • scope: base session grouping strategy for group-chat contexts.
    • per-sender (default): each sender gets an isolated session within a channel context.
    • global: all participants in a channel context share a single session (use only when shared context is intended).
  • dmScope: how DMs are grouped.
    • main: all DMs share the main session.
    • per-peer: isolate by sender id across channels.
    • per-channel-peer: isolate per channel + sender (recommended for multi-user inboxes).
    • per-account-channel-peer: isolate per account + channel + sender (recommended for multi-account).
  • identityLinks: map canonical ids to provider-prefixed peers for cross-channel session sharing.
  • reset: primary reset policy. daily resets at atHour local time; idle resets after idleMinutes. When both configured, whichever expires first wins.
  • resetByType: per-type overrides (direct, group, thread). Legacy dm accepted as alias for direct.
  • parentForkMaxTokens: max parent-session totalTokens allowed when creating a forked thread session (default 100000).
    • If parent totalTokens is above this value, OpenClaw starts a fresh thread session instead of inheriting parent transcript history.
    • Set 0 to disable this guard and always allow parent forking.
  • mainKey: legacy field. Runtime always uses "main" for the main direct-chat bucket.
  • agentToAgent.maxPingPongTurns: maximum reply-back turns between agents during agent-to-agent exchanges (integer, range: 05). 0 disables ping-pong chaining.
  • sendPolicy: match by channel, chatType (direct|group|channel, with legacy dm alias), keyPrefix, or rawKeyPrefix. First deny wins.
  • maintenance: session-store cleanup + retention controls.
    • mode: warn emits warnings only; enforce applies cleanup.
    • pruneAfter: age cutoff for stale entries (default 30d).
    • maxEntries: maximum number of entries in sessions.json (default 500).
    • rotateBytes: rotate sessions.json when it exceeds this size (default 10mb).
    • resetArchiveRetention: retention for *.reset.<timestamp> transcript archives. Defaults to pruneAfter; set false to disable.
    • maxDiskBytes: optional sessions-directory disk budget. In warn mode it logs warnings; in enforce mode it removes oldest artifacts/sessions first.
    • highWaterBytes: optional target after budget cleanup. Defaults to 80% of maxDiskBytes.
  • threadBindings: global defaults for thread-bound session features.
    • enabled: master default switch (providers can override; Discord uses channels.discord.threadBindings.enabled)
    • idleHours: default inactivity auto-unfocus in hours (0 disables; providers can override)
    • maxAgeHours: default hard max age in hours (0 disables; providers can override)

Messages

{
  messages: {
    responsePrefix: "🦞", // or "auto"
    ackReaction: "👀",
    ackReactionScope: "group-mentions", // group-mentions | group-all | direct | all
    removeAckAfterReply: false,
    queue: {
      mode: "collect", // steer | followup | collect | steer-backlog | steer+backlog | queue | interrupt
      debounceMs: 1000,
      cap: 20,
      drop: "summarize", // old | new | summarize
      byChannel: {
        whatsapp: "collect",
        telegram: "collect",
      },
    },
    inbound: {
      debounceMs: 2000, // 0 disables
      byChannel: {
        whatsapp: 5000,
        slack: 1500,
      },
    },
  },
}

Response prefix

Per-channel/account overrides: channels.<channel>.responsePrefix, channels.<channel>.accounts.<id>.responsePrefix. Resolution (most specific wins): account → channel → global. "" disables and stops cascade. "auto" derives [{identity.name}]. Template variables:
VariableDescriptionExample
{model}Short model nameclaude-opus-4-6
{modelFull}Full model identifieranthropic/claude-opus-4-6
{provider}Provider nameanthropic
{thinkingLevel}Current thinking levelhigh, low, off
{identity.name}Agent identity name(same as "auto")
Variables are case-insensitive. {think} is an alias for {thinkingLevel}.

Ack reaction

  • Defaults to active agent’s identity.emoji, otherwise "👀". Set "" to disable.
  • Per-channel overrides: channels.<channel>.ackReaction, channels.<channel>.accounts.<id>.ackReaction.
  • Resolution order: account → channel → messages.ackReaction → identity fallback.
  • Scope: group-mentions (default), group-all, direct, all.
  • removeAckAfterReply: removes ack after reply on Slack, Discord, and Telegram.
  • messages.statusReactions.enabled: enables lifecycle status reactions on Slack, Discord, and Telegram. On Slack and Discord, unset keeps status reactions enabled when ack reactions are active. On Telegram, set it explicitly to true to enable lifecycle status reactions.

Inbound debounce

Batches rapid text-only messages from the same sender into a single agent turn. Media/attachments flush immediately. Control commands bypass debouncing.

TTS (text-to-speech)

{
  messages: {
    tts: {
      auto: "always", // off | always | inbound | tagged
      mode: "final", // final | all
      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",
        baseUrl: "https://api.openai.com/v1",
        model: "gpt-4o-mini-tts",
        voice: "alloy",
      },
    },
  },
}
  • auto controls the default auto-TTS mode: off, always, inbound, or tagged. /tts on|off can override local prefs, and /tts status shows the effective state.
  • summaryModel overrides agents.defaults.model.primary for auto-summary.
  • modelOverrides is enabled by default; modelOverrides.allowProvider defaults to false (opt-in).
  • API keys fall back to ELEVENLABS_API_KEY/XI_API_KEY and OPENAI_API_KEY.
  • openai.baseUrl overrides the OpenAI TTS endpoint. Resolution order is config, then OPENAI_TTS_BASE_URL, then https://api.openai.com/v1.
  • When openai.baseUrl points to a non-OpenAI endpoint, OpenClaw treats it as an OpenAI-compatible TTS server and relaxes model/voice validation.

Talk

Defaults for Talk mode (macOS/iOS/Android).
{
  talk: {
    provider: "elevenlabs",
    providers: {
      elevenlabs: {
        voiceId: "elevenlabs_voice_id",
        voiceAliases: {
          Clawd: "EXAVITQu4vr4xnSDxMaL",
          Roger: "CwhRBWXzGAHq8TQ4Fs17",
        },
        modelId: "eleven_v3",
        outputFormat: "mp3_44100_128",
        apiKey: "elevenlabs_api_key",
      },
    },
    silenceTimeoutMs: 1500,
    interruptOnSpeech: true,
  },
}
  • talk.provider must match a key in talk.providers when multiple Talk providers are configured.
  • Legacy flat Talk keys (talk.voiceId, talk.voiceAliases, talk.modelId, talk.outputFormat, talk.apiKey) are compatibility-only and are auto-migrated into talk.providers.<provider>.
  • Voice IDs fall back to ELEVENLABS_VOICE_ID or SAG_VOICE_ID.
  • providers.*.apiKey accepts plaintext strings or SecretRef objects.
  • ELEVENLABS_API_KEY fallback applies only when no Talk API key is configured.
  • providers.*.voiceAliases lets Talk directives use friendly names.
  • silenceTimeoutMs controls how long Talk mode waits after user silence before it sends the transcript. Unset keeps the platform default pause window (700 ms on macOS and Android, 900 ms on iOS).