mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(config): address final layout review
This commit is contained in:
+3
-3
@@ -31,7 +31,7 @@ openclaw config get browser.executablePath
|
||||
openclaw config set browser.executablePath "/usr/bin/google-chrome"
|
||||
openclaw config set browser.profiles.work.executablePath "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
openclaw config set agents.defaults.heartbeat.every "2h"
|
||||
openclaw config set 'agents.entries[0].tools.exec.node' "node-id-or-name"
|
||||
openclaw config set 'agents.entries.main.tools.exec.node' "node-id-or-name"
|
||||
openclaw config set agents.defaults.models '{"openai/gpt-5.4":{}}' --strict-json --merge
|
||||
openclaw config set channels.discord.token --ref-provider default --ref-source env --ref-id DISCORD_BOT_TOKEN
|
||||
openclaw config set secrets.providers.vaultfile --provider-source file --provider-path /etc/openclaw/secrets.json --provider-mode json
|
||||
@@ -48,9 +48,9 @@ Dot or bracket notation. Quote bracket paths in shell examples so zsh does not g
|
||||
|
||||
```bash
|
||||
openclaw config get agents.defaults.workspace
|
||||
openclaw config get 'agents.entries[0].id'
|
||||
openclaw config get agents.entries.main
|
||||
openclaw config get agents.entries
|
||||
openclaw config set 'agents.entries[1].tools.exec.node' "node-id-or-name"
|
||||
openclaw config set 'agents.entries.work.tools.exec.node' "node-id-or-name"
|
||||
```
|
||||
|
||||
### `config get`
|
||||
|
||||
+5
-5
@@ -331,13 +331,13 @@ Every scope present in `policy.jsonc` must be valid and enforceable.
|
||||
| `gateway.remote.allow` | Remote Gateway mode/config | Set to `false` to deny remote Gateway mode. |
|
||||
| `gateway.http.denyEndpoints` | Gateway HTTP API endpoints | Deny endpoint ids such as `chatCompletions` or `responses`. |
|
||||
| `gateway.http.requireUrlAllowlists` | Gateway HTTP URL-fetch inputs | Set to `true` to require URL allowlists on URL-fetch inputs. |
|
||||
| `gateway.nodes.denyCommands` | `gateway.nodes.denyCommands` | Require exact node command ids such as `system.run` to be denied in OpenClaw config. |
|
||||
| `gateway.nodes.commands.deny` | `gateway.nodes.commands.deny` | Require exact node command ids such as `system.run` to be denied in OpenClaw config. |
|
||||
|
||||
`gateway.nodes.denyCommands` is an exact, case-sensitive deny-superset rule.
|
||||
`gateway.nodes.commands.deny` is an exact, case-sensitive deny-superset rule.
|
||||
Use it when policy must prove that privileged node commands are explicitly
|
||||
denied by OpenClaw config. A deployment that intentionally allows a privileged
|
||||
node command should update `policy.jsonc` after review instead of relying on
|
||||
`gateway.nodes.allowCommands` alone.
|
||||
`gateway.nodes.commands.allow` alone.
|
||||
|
||||
#### Agent workspace
|
||||
|
||||
@@ -912,7 +912,7 @@ Example findings:
|
||||
"ocPath": "oc://openclaw.config/gateway/nodes/denyCommands",
|
||||
"target": "oc://openclaw.config/gateway/nodes/denyCommands",
|
||||
"requirement": "oc://policy.jsonc/gateway/nodes/denyCommands",
|
||||
"fixHint": "Add 'system.run' to gateway.nodes.denyCommands or update policy after review."
|
||||
"fixHint": "Add 'system.run' to gateway.nodes.commands.deny or update policy after review."
|
||||
}
|
||||
```
|
||||
|
||||
@@ -978,7 +978,7 @@ allowlist values.
|
||||
Gateway bind and node-command findings stay review-required. When
|
||||
`policy/gateway-non-loopback-bind` or `policy/gateway-node-command-denied`
|
||||
can be mapped to a config path, `doctor --fix` reports the proposed
|
||||
`gateway.bind` or `gateway.nodes.denyCommands` change as skipped preview
|
||||
`gateway.bind` or `gateway.nodes.commands.deny` change as skipped preview
|
||||
guidance. It does not apply the change, and the finding does not count as
|
||||
repaired until an operator reviews and updates config or policy.
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ Run `openclaw doctor --fix` to rotate a persisted reused `hooks.token`, then upd
|
||||
**Sandbox/tools**
|
||||
|
||||
- Warns when sandbox Docker settings are configured while sandbox mode is off.
|
||||
- Warns when `gateway.nodes.denyCommands` uses ineffective pattern-like/unknown entries (matching is exact node command-name only, not shell-text filtering).
|
||||
- Warns when `gateway.nodes.allowCommands` explicitly enables dangerous node commands.
|
||||
- Warns when `gateway.nodes.commands.deny` uses ineffective pattern-like/unknown entries (matching is exact node command-name only, not shell-text filtering).
|
||||
- Warns when `gateway.nodes.commands.allow` explicitly enables dangerous node commands.
|
||||
- Warns when global `tools.profile="minimal"` is overridden by agent tool profiles.
|
||||
- Warns when write/edit tools are disabled but `exec` is still available without a constraining sandbox filesystem boundary.
|
||||
- Warns when open DMs or groups expose runtime/filesystem tools without sandbox/workspace guards.
|
||||
|
||||
@@ -139,7 +139,7 @@ Details: [Thinking + reasoning directives](/tools/thinking) and [Token use](/ref
|
||||
|
||||
## Prefixes, threading, and replies
|
||||
|
||||
- Outbound prefix cascade: `messages.responsePrefix`, `channels.<channel>.responsePrefix`, `channels.<channel>.accounts.<id>.responsePrefix`. WhatsApp also has `channels.whatsapp.messagePrefix` for an inbound prefix.
|
||||
- Outbound prefix cascade: `messages.responsePrefix`, `channels.<channel>.responsePrefix`, `channels.<channel>.accounts.<id>.responsePrefix`. WhatsApp also has `channels.whatsapp.responsePrefix` for an inbound prefix.
|
||||
- Reply threading via `replyToMode` and per-channel defaults.
|
||||
|
||||
Details: [Configuration](/gateway/config-agents#messages) and channel docs.
|
||||
|
||||
@@ -60,7 +60,7 @@ Related model-config surfaces:
|
||||
- `agents.defaults.utilityModel` is an optional lower-cost model for short internal tasks such as generated dashboard session titles, supported channel thread/topic titles, and progress narration. Per-agent `agents.entries.*.utilityModel` overrides it. When unset, OpenClaw uses the primary provider's declared small-model default when one exists (OpenAI → `gpt-5.6-luna`, Anthropic → `claude-haiku-4-5`), otherwise the agent's primary model; set it to an empty string to disable utility routing. Utility tasks are separate model calls and may send bounded task content to the selected model provider.
|
||||
- `agents.defaults.imageModel` is used only when the primary model cannot accept images.
|
||||
- `agents.defaults.pdfModel` is used by the `pdf` tool. If unset, the tool falls back to `imageModel`, then the resolved session/default model.
|
||||
- `agents.defaults.imageGenerationModel`, `musicGenerationModel`, and `videoGenerationModel` back the shared media-generation tools. If unset, each tool infers an auth-backed provider default: current default provider first, then the remaining registered providers for that capability in provider-id order. Set `agents.defaults.mediaGenerationAutoProviderFallback: false` to disable that cross-provider inference while keeping explicit fallbacks.
|
||||
- `agents.defaults.mediaModels.{image,music,video}` backs the shared media-generation tools. If unset, each tool infers an auth-backed provider default: current default provider first, then the remaining registered providers for that capability in provider-id order. Cross-provider fallback is the fixed default behavior.
|
||||
- Per-agent `agents.entries.*.model` (plus bindings) overrides `agents.defaults.model` — see [Multi-agent routing](/concepts/multi-agent).
|
||||
|
||||
Full key reference, defaults, and JSON5 examples: [Configuration reference](/gateway/config-agents#agent-defaults).
|
||||
|
||||
@@ -12,24 +12,24 @@ OpenClaw runs shell commands through the `exec` tool and keeps long-running task
|
||||
|
||||
Parameters:
|
||||
|
||||
| Parameter | Description |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `command` | Required. Shell command to run. |
|
||||
| `workdir` | Working directory; omit to use the default cwd. |
|
||||
| `env` | Extra environment variables for the command. |
|
||||
| `yieldMs` | Milliseconds to wait before backgrounding (default 10000). |
|
||||
| `background` | Run in background immediately. |
|
||||
| `timeout` | Timeout in seconds (default `tools.exec.timeoutSec`); kills the process on expiry. Set `timeout: 0` to disable the exec process timeout for that call. |
|
||||
| `pty` | Run in a pseudo-terminal when available (TTY-required CLIs, coding agents). |
|
||||
| `elevated` | Run outside the sandbox if elevated mode is enabled/allowed (`gateway` by default, or `node` when the exec target is `node`). |
|
||||
| `host` | Exec target: `auto`, `sandbox`, `gateway`, or `node`. |
|
||||
| `node` | Node id/name, used with `host: "node"`. |
|
||||
| Parameter | Description |
|
||||
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `command` | Required. Shell command to run. |
|
||||
| `workdir` | Working directory; omit to use the default cwd. |
|
||||
| `env` | Extra environment variables for the command. |
|
||||
| `yieldMs` | Milliseconds to wait before backgrounding (default 10000). |
|
||||
| `background` | Run in background immediately. |
|
||||
| `timeout` | Timeout in seconds (default `tools.exec.timeoutSeconds`); kills the process on expiry. Set `timeout: 0` to disable the exec process timeout for that call. |
|
||||
| `pty` | Run in a pseudo-terminal when available (TTY-required CLIs, coding agents). |
|
||||
| `elevated` | Run outside the sandbox if elevated mode is enabled/allowed (`gateway` by default, or `node` when the exec target is `node`). |
|
||||
| `host` | Exec target: `auto`, `sandbox`, `gateway`, or `node`. |
|
||||
| `node` | Node id/name, used with `host: "node"`. |
|
||||
|
||||
Behavior:
|
||||
|
||||
- Foreground runs return output directly.
|
||||
- When backgrounded (explicit or via `yieldMs` timeout), the tool returns `status: "running"` + `sessionId` and a short output tail.
|
||||
- Backgrounded and `yieldMs` runs inherit `tools.exec.timeoutSec` unless the call passes an explicit `timeout`.
|
||||
- Backgrounded and `yieldMs` runs inherit `tools.exec.timeoutSeconds` unless the call passes an explicit `timeout`.
|
||||
- Output stays in memory until the session is polled or cleared.
|
||||
- If the `process` tool is disallowed, `exec` runs synchronously and ignores `yieldMs`/`background`.
|
||||
- Spawned exec commands receive `OPENCLAW_SHELL=exec` for context-aware shell/profile rules.
|
||||
@@ -52,7 +52,7 @@ Behavior:
|
||||
| Key | Default | Effect |
|
||||
| ------------------------------------- | ------- | ------------------------------------------------------------------------------- |
|
||||
| `tools.exec.backgroundMs` | 10000 | Same as `OPENCLAW_BASH_YIELD_MS`. |
|
||||
| `tools.exec.timeoutSec` | 1800 | Default per-call timeout. |
|
||||
| `tools.exec.timeoutSeconds` | 1800 | Default per-call timeout. |
|
||||
| `tools.exec.cleanupMs` | 1800000 | Same as `OPENCLAW_BASH_JOB_TTL_MS`. |
|
||||
| `tools.exec.notifyOnExit` | true | Enqueue a system event + request heartbeat when a backgrounded exec exits. |
|
||||
| `tools.exec.notifyOnExitEmptySuccess` | false | Also enqueue completion events for successful backgrounded runs with no output. |
|
||||
|
||||
@@ -369,20 +369,22 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
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"],
|
||||
},
|
||||
videoGenerationModel: {
|
||||
primary: "qwen/wan2.6-t2v",
|
||||
fallbacks: ["qwen/wan2.6-i2v"],
|
||||
mediaModels: {
|
||||
image: {
|
||||
primary: "openai/gpt-image-2",
|
||||
fallbacks: ["google/gemini-3.1-flash-image"],
|
||||
},
|
||||
video: {
|
||||
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
|
||||
pdfMaxBytesMb: 10,
|
||||
pdfMaxMb: 10,
|
||||
pdfMaxPages: 20,
|
||||
thinkingDefault: "low",
|
||||
verboseDefault: "off",
|
||||
@@ -406,17 +408,17 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
- Used by the `image` tool path as its vision-model config when the active model cannot accept images. Native-vision models receive loaded image bytes directly instead.
|
||||
- Also used as fallback routing when the selected/default model cannot accept image input.
|
||||
- Prefer explicit `provider/model` refs. Bare IDs are accepted for compatibility; if a bare ID uniquely matches a configured image-capable entry in `models.providers.*.models`, OpenClaw qualifies it to that provider. Ambiguous configured matches require an explicit provider prefix.
|
||||
- `imageGenerationModel`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
|
||||
- `mediaModels.image`: 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` for native Gemini image generation, `fal/fal-ai/flux/dev` for fal, `openai/gpt-image-2` for OpenAI Images, or `openai/gpt-image-1.5` for transparent-background OpenAI PNG/WebP output.
|
||||
- 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` / `openai/gpt-image-1.5`, `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 }`).
|
||||
- `mediaModels.music`: 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.6`.
|
||||
- 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 }`).
|
||||
- `mediaModels.video`: 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.
|
||||
@@ -425,7 +427,7 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
- `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.
|
||||
- `pdfMaxMb`: 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"`.
|
||||
- `toolProgressDetail`: detail mode for `/verbose` tool summaries and progress-draft tool lines. Values: `"explain"` (default, compact human labels) or `"raw"` (append raw command/detail when available). Per-agent `agents.entries.*.toolProgressDetail` overrides this default.
|
||||
@@ -1377,7 +1379,7 @@ Batches rapid text-only messages from the same sender into a single agent turn.
|
||||
|
||||
### Other message keys
|
||||
|
||||
- `channels.whatsapp.messagePrefix`: WhatsApp-only prefix prepended to inbound user messages before they reach the agent runtime.
|
||||
- `channels.whatsapp.responsePrefix`: WhatsApp-only prefix prepended to inbound user messages before they reach the agent runtime.
|
||||
- `messages.visibleReplies`: controls visible source replies across direct, group, and channel conversations (`"message_tool"` requires `message(action=send)` for visible output; `"automatic"` posts normal replies as before).
|
||||
- `messages.usageTemplate` / `messages.responseUsage`: custom `/usage` footer template and default per-reply usage mode (`off | tokens | full`, plus legacy `on` alias for `tokens`).
|
||||
- `messages.groupChat.mentionPatterns` / `historyLimit`: group-message mention triggers and history window sizing.
|
||||
|
||||
@@ -666,7 +666,7 @@ See [Plugins](/tools/plugin).
|
||||
- `allowRealIpFallback`: when `true`, the gateway accepts `X-Real-IP` if `X-Forwarded-For` is missing. Default `false` for fail-closed behavior.
|
||||
- `gateway.nodes.pairing.autoApproveCidrs`: optional CIDR/IP allowlist for auto-approving first-time node device pairing with no requested scopes. It is disabled when unset. This does not auto-approve operator/browser/Control UI/WebChat pairing, and it does not auto-approve role, scope, metadata, or public-key upgrades.
|
||||
- `gateway.nodes.pairing.sshVerify`: SSH-verified auto-approval for first-time node device pairing (default: enabled). The gateway SSHes back to the pairing host (BatchMode, strict host keys) and approves only on an exact `openclaw node identity` device-key match. Same eligibility floor as `autoApproveCidrs`; probes are limited to private/CGNAT source addresses unless `cidrs` overrides them. Set `false` to disable, or `{ user, identity, timeoutMs, cidrs }` to tune. See [Node pairing](/gateway/pairing#ssh-verified-device-auto-approval-default).
|
||||
- `gateway.nodes.allowCommands` / `gateway.nodes.denyCommands`: global allow/deny shaping for declared node commands after pairing and platform allowlist evaluation. Use `allowCommands` to opt into dangerous node commands such as `camera.snap`, `camera.clip`, `screen.record`, `health.summary`, `sms.search`, and `sms.send`; `denyCommands` removes a command even if a platform default or explicit allow would otherwise include it. iOS Health permission, Android SMS permission, and Gateway command authorization are independent. After a node changes its declared command list, reject and re-approve that device pairing so the gateway stores the updated command snapshot.
|
||||
- `gateway.nodes.commands.allow` / `gateway.nodes.commands.deny`: global allow/deny shaping for declared node commands after pairing and platform allowlist evaluation. Use `allowCommands` to opt into dangerous node commands such as `camera.snap`, `camera.clip`, `screen.record`, `health.summary`, `sms.search`, and `sms.send`; `denyCommands` removes a command even if a platform default or explicit allow would otherwise include it. iOS Health permission, Android SMS permission, and Gateway command authorization are independent. After a node changes its declared command list, reject and re-approve that device pairing so the gateway stores the updated command snapshot.
|
||||
- `gateway.tools.deny`: extra tool names blocked for HTTP `POST /tools/invoke` (extends default deny list).
|
||||
- `gateway.tools.allow`: remove tool names from the default HTTP deny list for
|
||||
owner/admin callers. This does not upgrade identity-bearing `operator.write`
|
||||
|
||||
@@ -98,7 +98,7 @@ Notes:
|
||||
Node pairing approval records the trusted capability surface. It does **not** pin the live node command surface per node.
|
||||
|
||||
- Live node commands come from what the node declares on connect, filtered by
|
||||
the gateway's global node command policy (`gateway.nodes.allowCommands` and
|
||||
the gateway's global node command policy (`gateway.nodes.commands.allow` and
|
||||
`denyCommands`).
|
||||
- Per-node `system.run` allow and ask policy lives on the node in
|
||||
`exec.approvals.node.*`, not in the pairing record.
|
||||
|
||||
@@ -336,7 +336,7 @@ path; skills are not accepted in `connect` params. Each descriptor contains a
|
||||
safe name, description, and bounded `SKILL.md` content. The Gateway parses that
|
||||
content with the normal skills loader, includes it in agent skill snapshots
|
||||
while the node is connected, and removes it on disconnect. Set
|
||||
`gateway.nodes.skills.enabled: false` to ignore node-published skills.
|
||||
`gateway.nodes.allowSkills: false` to ignore node-published skills.
|
||||
|
||||
## Presence
|
||||
|
||||
@@ -584,7 +584,7 @@ methods. Treat this as feature discovery, not a full enumeration of
|
||||
- `node.rename` updates a paired node label.
|
||||
- `node.invoke` forwards a command to a connected node.
|
||||
- `node.invoke.result` returns the result for an invoke request.
|
||||
- `mcp.tools.call.v1` is the headless node-host command for calling a configured node-local MCP tool. It is carried through `node.invoke`, requires the node to declare the command, and remains subject to pairing approval and `gateway.nodes.denyCommands`.
|
||||
- `mcp.tools.call.v1` is the headless node-host command for calling a configured node-local MCP tool. It is carried through `node.invoke`, requires the node to declare the command, and remains subject to pairing approval and `gateway.nodes.commands.deny`.
|
||||
- `node.event` carries node-originated events back into the gateway.
|
||||
- `node.pluginTools.update` is the only publication path for replacing the connected node's agent-visible plugin/MCP tool descriptors; `connect` params do not carry them.
|
||||
- `node.pending.pull` and `node.pending.ack` are the connected-node queue APIs.
|
||||
|
||||
@@ -47,8 +47,8 @@ exhaustive):
|
||||
| `gateway.http.no_auth` | warn/critical | Gateway HTTP APIs reachable with `auth.mode="none"` | `gateway.auth.mode`, `gateway.http.endpoints.*`, `plugins.entries.admin-http-rpc` | no |
|
||||
| `gateway.http.session_key_override_enabled` | info | HTTP API callers can override `sessionKey` | `gateway.http.allowSessionKeyOverride` | no |
|
||||
| `gateway.tools_invoke_http.dangerous_allow` | warn/critical | Re-enables dangerous tools over HTTP API for owner/admin callers | `gateway.tools.allow` | no |
|
||||
| `gateway.nodes.allow_commands_dangerous` | warn/critical | Enables high-impact node commands (desktop input/camera/screen/contacts/calendar/SMS) | `gateway.nodes.allowCommands` | no |
|
||||
| `gateway.nodes.deny_commands_ineffective` | warn | Pattern-like deny entries do not match shell text or groups | `gateway.nodes.denyCommands` | no |
|
||||
| `gateway.nodes.allow_commands_dangerous` | warn/critical | Enables high-impact node commands (desktop input/camera/screen/contacts/calendar/SMS) | `gateway.nodes.commands.allow` | no |
|
||||
| `gateway.nodes.deny_commands_ineffective` | warn | Pattern-like deny entries do not match shell text or groups | `gateway.nodes.commands.deny` | no |
|
||||
| `gateway.tailscale_funnel` | critical | Public internet exposure | `gateway.tailscale.mode` | no |
|
||||
| `gateway.tailscale_serve` | info | Tailnet exposure is enabled via Serve | `gateway.tailscale.mode` | no |
|
||||
| `gateway.control_ui.allowed_origins_required` | critical | Non-loopback Control UI without explicit browser-origin allowlist | `gateway.controlUi.allowedOrigins` | no |
|
||||
|
||||
@@ -51,7 +51,7 @@ openclaw security audit --json
|
||||
- **Browser control exposure** - remote nodes, relay ports, remote CDP endpoints.
|
||||
- **Local disk hygiene** - permissions, symlinks, config includes, synced-folder paths.
|
||||
- **Plugins** - loading without an explicit allowlist.
|
||||
- **Policy drift** - sandbox Docker settings configured but sandbox mode off; `gateway.nodes.denyCommands` entries that look effective but only match exact command IDs (for example `system.run`), not shell text inside the payload; dangerous `gateway.nodes.allowCommands` entries; global `tools.profile="minimal"` overridden per agent; plugin-owned tools reachable under a permissive policy.
|
||||
- **Policy drift** - sandbox Docker settings configured but sandbox mode off; `gateway.nodes.commands.deny` entries that look effective but only match exact command IDs (for example `system.run`), not shell text inside the payload; dangerous `gateway.nodes.commands.allow` entries; global `tools.profile="minimal"` overridden per agent; plugin-owned tools reachable under a permissive policy.
|
||||
- **Runtime expectation drift** - assuming implicit exec still means `sandbox` when `tools.exec.host` now defaults to `auto`, or setting `tools.exec.host="sandbox"` while sandbox mode is off.
|
||||
- **Model hygiene** - warns on legacy configured models (soft warning, not a hard block).
|
||||
|
||||
@@ -311,7 +311,7 @@ For any agent/surface handling untrusted content, deny these by default:
|
||||
If a macOS node is paired, the Gateway can invoke `system.run` on it - this is remote code execution on that Mac.
|
||||
|
||||
- Requires node pairing (approval + token). Pairing establishes node identity/trust and token issuance; it is not a per-command approval surface.
|
||||
- The Gateway applies a coarse global node command policy via `gateway.nodes.allowCommands` / `denyCommands`. `denyCommands` matches exact node command names only (for example `system.run`), not shell text inside a command payload - a reconnecting node advertising a different command list is not, by itself, a vulnerability if the gateway global policy and the node's own exec approvals still enforce the boundary.
|
||||
- The Gateway applies a coarse global node command policy via `gateway.nodes.commands.allow` / `denyCommands`. `denyCommands` matches exact node command names only (for example `system.run`), not shell text inside a command payload - a reconnecting node advertising a different command list is not, by itself, a vulnerability if the gateway global policy and the node's own exec approvals still enforce the boundary.
|
||||
- The per-node `system.run` policy is the node's own exec approvals file (`exec.approvals.node.*`), controlled on the Mac via Settings -> Exec approvals (security + ask + allowlist); it can be stricter or looser than the gateway's global command-ID policy.
|
||||
- A node running `security="full"` and `ask="off"` follows the default trusted-operator model - expected behavior, not a bug, unless your deployment needs a tighter stance.
|
||||
- Approval mode binds exact request context and, when possible, one concrete local script/file operand. If OpenClaw cannot identify exactly one direct local file for an interpreter/runtime command, approval-backed execution is denied rather than promising full semantic coverage.
|
||||
|
||||
@@ -154,7 +154,7 @@ Linux returns capture-capable, readable V4L2 device paths from `camera.list`; FF
|
||||
|
||||
The plugin uses `libx264` for MP4 video and does not silently change codecs. An FFmpeg build without the required input or encoders returns `CAMERA_UNAVAILABLE`. Photos and clips that would exceed the 25MB base64 payload budget fail with `PAYLOAD_TOO_LARGE`.
|
||||
|
||||
`camera.snap` and `camera.clip` remain dangerous commands. Add them to `gateway.nodes.allowCommands` only when you intend to arm capture; enabling the plugin alone does not bypass Gateway policy.
|
||||
`camera.snap` and `camera.clip` remain dangerous commands. Add them to `gateway.nodes.commands.allow` only when you intend to arm capture; enabling the plugin alone does not bypass Gateway policy.
|
||||
|
||||
## Safety + practical limits
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ Reads reuse `screen.snapshot`; there is no second capture path. See [Camera and
|
||||
|
||||
Arming requires `operator.admin` (or the owner) and auto-expires. The legacy `/phone arm all` group intentionally excludes desktop control; use the explicit `computer` group. Arming only toggles what the gateway may invoke; the macOS app still enforces its **Allow Computer Control** setting and OS permissions.
|
||||
|
||||
For persistent authorization, add `computer.act` to `gateway.nodes.allowCommands` **and remove it from** `gateway.nodes.denyCommands`; the deny list wins. Persistent authorization does not auto-expire. Entries already present before `/phone arm` remain after `/phone disarm`; do not convert a temporary grant to persistent while it is armed.
|
||||
For persistent authorization, add `computer.act` to `gateway.nodes.commands.allow` **and remove it from** `gateway.nodes.commands.deny`; the deny list wins. Persistent authorization does not auto-expire. Entries already present before `/phone arm` remain after `/phone disarm`; do not convert a temporary grant to persistent while it is armed.
|
||||
|
||||
Authorization is deliberately split between enabling and use. Arming or
|
||||
persistently configuring `computer.act` requires administrative authority.
|
||||
|
||||
+12
-12
@@ -193,7 +193,7 @@ the node host does not watch this config.
|
||||
Gateway operators can ignore all agent-visible tools published by paired nodes,
|
||||
including node-hosted MCP tools, with
|
||||
`gateway.nodes.pluginTools.enabled: false`. Exact command denies such as
|
||||
`gateway.nodes.denyCommands: ["mcp.tools.call.v1"]` also block execution.
|
||||
`gateway.nodes.commands.deny: ["mcp.tools.call.v1"]` also block execution.
|
||||
|
||||
### Node-hosted skills
|
||||
|
||||
@@ -227,7 +227,7 @@ out of that agent's snapshot.
|
||||
|
||||
Set `nodeHost.skills.enabled: false` on the node to stop publication. Gateway
|
||||
operators can ignore skills from every paired node with
|
||||
`gateway.nodes.skills.enabled: false`.
|
||||
`gateway.nodes.allowSkills: false`.
|
||||
|
||||
### Headless identity state
|
||||
|
||||
@@ -466,9 +466,9 @@ These rows describe the Gateway policy ceiling, not the commands implemented by
|
||||
|
||||
Desktop host commands (`system.run`, `system.run.prepare`, `system.which`, `browser.proxy`, `mcp.tools.call.v1`, and `screen.snapshot` on macOS/Windows) are not part of the static platform-default table above. They become available once the operator approves a pairing request that declares them, after which the node's approved command set carries them forward on reconnect.
|
||||
|
||||
Dangerous or privacy-heavy commands still require explicit opt-in with `gateway.nodes.allowCommands`, even if a node declares them: `camera.snap`, `camera.clip`, `screen.record`, `computer.act`, `contacts.add`, `calendar.add`, `reminders.add`, `health.summary`, `sms.send`, `sms.search`. `gateway.nodes.denyCommands` always wins over defaults and extra allowlist entries. See [HealthKit summaries](/platforms/ios-healthkit) for the iPhone consent gate and [Computer use](/nodes/computer-use) for the additional macOS, tool-policy, and arming gates around desktop input.
|
||||
Dangerous or privacy-heavy commands still require explicit opt-in with `gateway.nodes.commands.allow`, even if a node declares them: `camera.snap`, `camera.clip`, `screen.record`, `computer.act`, `contacts.add`, `calendar.add`, `reminders.add`, `health.summary`, `sms.send`, `sms.search`. `gateway.nodes.commands.deny` always wins over defaults and extra allowlist entries. See [HealthKit summaries](/platforms/ios-healthkit) for the iPhone consent gate and [Computer use](/nodes/computer-use) for the additional macOS, tool-policy, and arming gates around desktop input.
|
||||
|
||||
Plugin-owned node commands can add a Gateway node-invoke policy. That policy runs after the allowlist check and before forwarding to the node, so raw `node.invoke`, CLI helpers, and dedicated agent tools share the same plugin permission boundary. Dangerous plugin node commands still require explicit `gateway.nodes.allowCommands` opt-in.
|
||||
Plugin-owned node commands can add a Gateway node-invoke policy. That policy runs after the allowlist check and before forwarding to the node, so raw `node.invoke`, CLI helpers, and dedicated agent tools share the same plugin permission boundary. Dangerous plugin node commands still require explicit `gateway.nodes.commands.allow` opt-in.
|
||||
|
||||
After a node changes its declared command list, reject the old device pairing and approve the new request so the gateway stores the updated command snapshot.
|
||||
|
||||
@@ -630,7 +630,7 @@ Notes:
|
||||
|
||||
## SMS (Android nodes)
|
||||
|
||||
Android nodes can expose `sms.send` and `sms.search` when the user grants **SMS** permission and the device supports telephony. Both commands are dangerous-by-default: the gateway operator must also add them to `gateway.nodes.allowCommands` before they can be invoked (see [Command policy](#command-policy)).
|
||||
Android nodes can expose `sms.send` and `sms.search` when the user grants **SMS** permission and the device supports telephony. Both commands are dangerous-by-default: the gateway operator must also add them to `gateway.nodes.commands.allow` before they can be invoked (see [Command policy](#command-policy)).
|
||||
|
||||
For read-only SMS search, opt in explicitly in `openclaw.json`:
|
||||
|
||||
@@ -656,7 +656,7 @@ Notes:
|
||||
|
||||
- `sms.search` may be declared before `READ_SMS` is granted so an invocation can return a permission diagnostic; reading messages still requires that Android permission.
|
||||
- Wi-Fi-only devices without telephony will not advertise `sms.send`.
|
||||
- A `requires explicit gateway.nodes.allowCommands opt-in` error means the phone declared the command but the Gateway operator has not authorized it.
|
||||
- A `requires explicit gateway.nodes.commands.allow opt-in` error means the phone declared the command but the Gateway operator has not authorized it.
|
||||
|
||||
## Device and personal data commands
|
||||
|
||||
@@ -669,9 +669,9 @@ Available families:
|
||||
- `device.apps` — Android, macOS, and headless-mac nodes. Android requires Installed Apps sharing in Settings and returns launcher-visible apps by default. TypeScript node hosts keep sharing off by default and accept `query`, `limit`, and `includeSystem`; macOS results contain `label`, `bundleId`, `path`, and `system`.
|
||||
- `notifications.list`, `notifications.actions` — Android only.
|
||||
- `photos.latest` — iOS, Android.
|
||||
- `contacts.search` — iOS, Android (read-only default); `contacts.add` is dangerous and needs `gateway.nodes.allowCommands`.
|
||||
- `calendar.events` — iOS, Android (read-only default); `calendar.add` is dangerous and needs `gateway.nodes.allowCommands`.
|
||||
- `reminders.list` — iOS, Android (read-only default); `reminders.add` is dangerous and needs `gateway.nodes.allowCommands`.
|
||||
- `contacts.search` — iOS, Android (read-only default); `contacts.add` is dangerous and needs `gateway.nodes.commands.allow`.
|
||||
- `calendar.events` — iOS, Android (read-only default); `calendar.add` is dangerous and needs `gateway.nodes.commands.allow`.
|
||||
- `reminders.list` — iOS, Android (read-only default); `reminders.add` is dangerous and needs `gateway.nodes.commands.allow`.
|
||||
- `callLog.search` — Android only.
|
||||
- `motion.activity`, `motion.pedometer` — iOS, Android; capability-gated by available sensors.
|
||||
|
||||
@@ -702,7 +702,7 @@ Notes:
|
||||
- `nodes invoke` does not expose `system.run` or `system.run.prepare`; those stay on the exec path only.
|
||||
- The exec path prepares a canonical `systemRunPlan` before approval. Once an approval is granted, the gateway forwards that stored plan, not any later caller-edited command/cwd/session fields.
|
||||
- `system.notify` respects notification permission state on the macOS app; supports `--priority <passive|active|timeSensitive>` and `--delivery <system|overlay|auto>`.
|
||||
- Unrecognized node `platform` / `deviceFamily` metadata uses a conservative default allowlist that excludes `system.run` and `system.which`. If you intentionally need those commands for an unknown platform, add them explicitly via `gateway.nodes.allowCommands`.
|
||||
- Unrecognized node `platform` / `deviceFamily` metadata uses a conservative default allowlist that excludes `system.run` and `system.which`. If you intentionally need those commands for an unknown platform, add them explicitly via `gateway.nodes.commands.allow`.
|
||||
- `system.run` supports `--cwd`, `--env KEY=VAL`, `--command-timeout`, and `--needs-screen-recording`.
|
||||
- For shell wrappers (`bash|sh|zsh ... -c/-lc`), request-scoped `--env` values are reduced to an explicit allowlist (`TERM`, `LANG`, `LC_*`, `COLORTERM`, `NO_COLOR`, `FORCE_COLOR`).
|
||||
- For allow-always decisions in allowlist mode, known dispatch wrappers (`env`, `flock`, `nice`, `nohup`, `stdbuf`, `timeout`) persist inner executable paths instead of wrapper paths. If unwrapping is not safe, no allowlist entry is persisted automatically.
|
||||
@@ -725,14 +725,14 @@ Per-agent override:
|
||||
|
||||
```bash
|
||||
openclaw config get agents.entries
|
||||
openclaw config set 'agents.entries[0].tools.exec.node' "node-id-or-name"
|
||||
openclaw config set 'agents.entries.main.tools.exec.node' "node-id-or-name"
|
||||
```
|
||||
|
||||
Unset to allow any node:
|
||||
|
||||
```bash
|
||||
openclaw config unset tools.exec.node
|
||||
openclaw config unset 'agents.entries[0].tools.exec.node'
|
||||
openclaw config unset 'agents.entries.main.tools.exec.node'
|
||||
```
|
||||
|
||||
## Permissions map
|
||||
|
||||
@@ -61,7 +61,7 @@ If you see `NODE_BACKGROUND_UNAVAILABLE`, bring the node app to the foreground a
|
||||
Three separate gates control whether a node command succeeds:
|
||||
|
||||
1. **Device pairing**: can this node connect to the gateway?
|
||||
2. **Gateway node command policy**: is the RPC command ID allowed by `gateway.nodes.allowCommands` / `denyCommands` and platform defaults?
|
||||
2. **Gateway node command policy**: is the RPC command ID allowed by `gateway.nodes.commands.allow` / `denyCommands` and platform defaults?
|
||||
3. **Exec approvals**: can this node run a specific shell command locally?
|
||||
|
||||
Node pairing is an identity/trust gate, not a per-command approval surface. For `system.run`, the per-node policy lives in that node's exec approvals file (`openclaw approvals get --node ...`), not in the gateway pairing record.
|
||||
@@ -112,7 +112,7 @@ If still stuck:
|
||||
- Re-grant OS permissions.
|
||||
- Recreate/adjust the exec approval policy.
|
||||
|
||||
For computer control, also verify that a vision-capable agent exposes the `computer` tool, `screen.snapshot` succeeds with Screen Recording permission, and `/phone status` shows the temporary or persistent gateway authorization you intended. A `gateway.nodes.denyCommands` entry always overrides `allowCommands`.
|
||||
For computer control, also verify that a vision-capable agent exposes the `computer` tool, `screen.snapshot` succeeds with Screen Recording permission, and `/phone status` shows the temporary or persistent gateway authorization you intended. A `gateway.nodes.commands.deny` entry always overrides `allowCommands`.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ authorization on the Gateway.
|
||||
|
||||
### 1. Authorize the Gateway command
|
||||
|
||||
Add `health.summary` to the existing `gateway.nodes.allowCommands` array in
|
||||
Add `health.summary` to the existing `gateway.nodes.commands.allow` array in
|
||||
`openclaw.json`. Preserve any commands already present:
|
||||
|
||||
```json5
|
||||
@@ -46,7 +46,7 @@ Add `health.summary` to the existing `gateway.nodes.allowCommands` array in
|
||||
```
|
||||
|
||||
`health.summary` is classified as privacy-heavy and is never allowed by the
|
||||
iOS platform default. An entry in `gateway.nodes.denyCommands` overrides the
|
||||
iOS platform default. An entry in `gateway.nodes.commands.deny` overrides the
|
||||
allow entry. See [Node command policy](/nodes#command-policy).
|
||||
|
||||
### 2. Enable sharing on the iOS device
|
||||
@@ -137,7 +137,7 @@ calculated, so the same minute is not counted twice.
|
||||
To stop sharing, return to **Apple Health Summaries** and tap **Turn Off Summaries**.
|
||||
The iOS device then removes the Health capability and `health.summary` command from its node
|
||||
surface. You can also remove `health.summary` from
|
||||
`gateway.nodes.allowCommands` to close the Gateway side of the gate.
|
||||
`gateway.nodes.commands.allow` to close the Gateway side of the gate.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -149,8 +149,8 @@ Run `openclaw nodes pending` and approve any capability update, then inspect
|
||||
|
||||
### Command requires explicit opt-in
|
||||
|
||||
Add `health.summary` to `gateway.nodes.allowCommands`. Also check that
|
||||
`gateway.nodes.denyCommands` does not contain it; the deny list wins.
|
||||
Add `health.summary` to `gateway.nodes.commands.allow`. Also check that
|
||||
`gateway.nodes.commands.deny` does not contain it; the deny list wins.
|
||||
|
||||
### `HEALTH_ACCESS_DISABLED`
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ A node can be connected and device-paired while its effective `caps` and `comman
|
||||
|
||||
Camera devices must be readable by the service user, commonly through the `video` group. Camera clips use the default PulseAudio or PipeWire source when `includeAudio` is true; microphone audio exists only as that clip track, not as a standalone command. Location requires the node-service user to be permitted by the host's GeoClue policy.
|
||||
|
||||
`camera.snap` and `camera.clip` also require explicit Gateway arming through `gateway.nodes.allowCommands`. See [Camera capture](/nodes/camera) and [Location command](/nodes/location-command) for payloads, limits, and errors.
|
||||
`camera.snap` and `camera.clip` also require explicit Gateway arming through `gateway.nodes.commands.allow`. See [Camera capture](/nodes/camera) and [Location command](/nodes/location-command) for payloads, limits, and errors.
|
||||
|
||||
## Install
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ openclaw nodes status
|
||||
|
||||
The Gateway only forwards commands the node declares and server policy
|
||||
allows. Privacy-sensitive commands such as `screen.record`, `camera.snap`,
|
||||
and `camera.clip` need explicit `gateway.nodes.allowCommands` opt-in.
|
||||
and `camera.clip` need explicit `gateway.nodes.commands.allow` opt-in.
|
||||
|
||||
## Local MCP mode
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ Image generation follows the standard shape:
|
||||
The config key is intentionally separate from vision-analysis routing:
|
||||
|
||||
- `agents.defaults.imageModel` analyzes images.
|
||||
- `agents.defaults.imageGenerationModel` generates images.
|
||||
- `agents.defaults.mediaModels.image` generates images.
|
||||
|
||||
Keep those separate so fallback and policy remain explicit.
|
||||
|
||||
|
||||
@@ -527,7 +527,7 @@ OpenClaw-owned dynamic tool calls are bounded independently from
|
||||
first available timeout in this order:
|
||||
|
||||
- A positive per-call `timeoutMs` argument.
|
||||
- For `image_generate`, `agents.defaults.imageGenerationModel.timeoutMs`.
|
||||
- For `image_generate`, `agents.defaults.mediaModels.image.timeoutMs`.
|
||||
- For `image_generate` without a configured timeout, the 120 second
|
||||
image-generation default.
|
||||
- For the media-understanding `image` tool, the selected image-capable `tools.media.models[]` entry's `timeoutSeconds`
|
||||
|
||||
@@ -317,7 +317,7 @@ writes an OpenClaw-owned session transcript tool result.
|
||||
|
||||
OpenClaw continues to own media delivery and media provider selection. Image,
|
||||
video, music, PDF, TTS, and media understanding use matching provider/model
|
||||
settings such as `agents.defaults.imageGenerationModel`,
|
||||
settings such as `agents.defaults.mediaModels.image`,
|
||||
`videoGenerationModel`, `pdfModel`, and `tts`.
|
||||
|
||||
Text, images, video, music, TTS, approvals, and messaging-tool output continue
|
||||
|
||||
@@ -764,7 +764,7 @@ OpenClaw-owned dynamic tool calls are bounded independently from
|
||||
`appServer.requestTimeoutMs`: Codex `item/tool/call` requests use a 90
|
||||
second OpenClaw watchdog by default. A positive per-call `timeoutMs`
|
||||
argument extends or shortens that specific tool budget, capped at 600000 ms.
|
||||
The `image_generate` tool uses `agents.defaults.imageGenerationModel.timeoutMs`
|
||||
The `image_generate` tool uses `agents.defaults.mediaModels.image.timeoutMs`
|
||||
when the tool call does not provide its own timeout, or a 120 second
|
||||
image-generation default otherwise. The media-understanding `image` tool
|
||||
uses the selected image-capable `tools.media.models[]` entry's `timeoutSeconds` or its 60 second media default; for
|
||||
|
||||
@@ -244,7 +244,7 @@ If `chromeNode.node` is omitted, OpenClaw auto-selects only when exactly one con
|
||||
| Symptom | Fix |
|
||||
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Configured Google Meet node ... is not usable: offline` | The pinned node is known but unavailable. Report the setup blocker; do not silently fall back to another transport unless asked. |
|
||||
| `No connected Google Meet-capable node` | Run `openclaw node run` in the VM, approve pairing, and run `openclaw plugins enable google-meet` and `openclaw plugins enable browser` there. Confirm `gateway.nodes.allowCommands` includes `googlemeet.chrome` and `browser.proxy`. |
|
||||
| `No connected Google Meet-capable node` | Run `openclaw node run` in the VM, approve pairing, and run `openclaw plugins enable google-meet` and `openclaw plugins enable browser` there. Confirm `gateway.nodes.commands.allow` includes `googlemeet.chrome` and `browser.proxy`. |
|
||||
| `BlackHole 2ch audio device not found` | Install `blackhole-2ch` on the host being checked and reboot. |
|
||||
| `BlackHole 2ch audio device not found on the node` | Install `blackhole-2ch` in the VM and reboot the VM. |
|
||||
| Chrome opens but cannot join | Sign in to the browser profile in the VM, or keep `chrome.guestName` set. Guest auto-join uses OpenClaw browser automation through the node browser proxy; point the node's `browser.defaultProfile` (or a named existing-session profile) at the profile you want. |
|
||||
|
||||
@@ -226,7 +226,7 @@ the derived-text methods directly.
|
||||
model when you need a fully local pipeline.
|
||||
- Frames, the timeline database, and temporary captures are written with
|
||||
owner-only file permissions.
|
||||
- Adding `screen.snapshot` to `gateway.nodes.denyCommands` is the
|
||||
- Adding `screen.snapshot` to `gateway.nodes.commands.deny` is the
|
||||
screen-capture kill switch: it blocks app-node capture and Logbook's own
|
||||
`logbook.snapshot` command alike.
|
||||
- Setting `tools.media.image.enabled: false` also stops Logbook from borrowing
|
||||
@@ -258,7 +258,7 @@ openclaw logs --follow
|
||||
- Confirm the node exposes `screen.snapshot` or `logbook.snapshot`.
|
||||
- Grant Screen Recording permission on the capture Mac.
|
||||
- If `nodeId` is configured, confirm it matches the node id or display name.
|
||||
- Check that `gateway.nodes.denyCommands` does not contain
|
||||
- Check that `gateway.nodes.commands.deny` does not contain
|
||||
`screen.snapshot`.
|
||||
|
||||
After three consecutive failures, Logbook backs off for ten capture ticks and
|
||||
|
||||
@@ -182,7 +182,7 @@ successful Gateway connect; the Gateway exposes it to agent runs only while that
|
||||
node is connected and only if the descriptor's `command` is in the node's
|
||||
approved command surface. Set `agentTool.defaultPlatforms` to opt a
|
||||
non-dangerous command into the default node command allowlist; otherwise require
|
||||
explicit `gateway.nodes.allowCommands` or a node-invoke policy. `agentTool.name`
|
||||
explicit `gateway.nodes.commands.allow` or a node-invoke policy. `agentTool.name`
|
||||
must be provider-safe: start with a letter, use only letters, digits,
|
||||
underscores, or hyphens, and stay within 64 characters. MCP-backed node tools
|
||||
can set `agentTool.mcp` metadata so catalog and tool-search surfaces can show
|
||||
|
||||
@@ -396,7 +396,7 @@ two-party event loops that do not go through the shared inbound reply runner.
|
||||
|
||||
Inside the Gateway this runtime is in-process. In plugin CLI commands it calls the configured Gateway over RPC, so commands such as `openclaw googlemeet recover-tab` can inspect paired nodes from the terminal. Node commands still go through normal Gateway node pairing, command allowlists, plugin node-invoke policies, and node-local command handling.
|
||||
|
||||
Plugins that expose node-hosted agent tools can set `agentTool.defaultPlatforms` for non-dangerous commands that should be allowlisted by default. Omit it when operators must opt in with `gateway.nodes.allowCommands`. Dangerous node-host commands should register a node-invoke policy with `api.registerNodeInvokePolicy(...)`; the policy runs in the Gateway after command allowlist checks and before the command is forwarded to the node, so direct `node.invoke` calls, node-hosted plugin tools, and higher-level plugin tools share the same enforcement path.
|
||||
Plugins that expose node-hosted agent tools can set `agentTool.defaultPlatforms` for non-dangerous commands that should be allowlisted by default. Omit it when operators must opt in with `gateway.nodes.commands.allow`. Dangerous node-host commands should register a node-invoke policy with `api.registerNodeInvokePolicy(...)`; the policy runs in the Gateway after command allowlist checks and before the command is forwarded to the node, so direct `node.invoke` calls, node-hosted plugin tools, and higher-level plugin tools share the same enforcement path.
|
||||
|
||||
<Warning>
|
||||
The optional `scopes` field requests Gateway operator scopes for the invocation. OpenClaw honors it only for bundled plugins and trusted official plugin installations; requests from other plugins do not elevate the call. Use it only when a trusted plugin must invoke a node command with a stricter Gateway scope, such as `operator.admin`.
|
||||
|
||||
@@ -54,15 +54,15 @@ live from `https://api.deepinfra.com/v1/openai/models?sort_by=openclaw&filter=wi
|
||||
once `DEEPINFRA_API_KEY` is configured. Other surfaces use the static
|
||||
defaults below until they move onto the same live catalog.
|
||||
|
||||
| Surface | Default model | OpenClaw config/tool |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
||||
| Chat / model provider | first chat-tagged entry from live catalog (static fallback `deepseek-ai/DeepSeek-V4-Flash`) | `agents.defaults.model` |
|
||||
| Image generation/editing | first `image-gen`-tagged entry from live catalog (static fallback `black-forest-labs/FLUX-1-schnell`) | `image_generate`, `agents.defaults.imageGenerationModel` |
|
||||
| Media understanding | `moonshotai/Kimi-K2.5` for images | inbound image understanding |
|
||||
| Speech-to-text | `openai/whisper-large-v3-turbo` | inbound audio transcription |
|
||||
| Text-to-speech | `hexgrad/Kokoro-82M` | `tts.provider: "deepinfra"` |
|
||||
| Video generation | static fallback `Pixverse/Pixverse-T2V` (no live video-gen rows from DeepInfra today) | `video_generate`, `agents.defaults.videoGenerationModel` |
|
||||
| Memory embeddings | `BAAI/bge-m3` | `memory.search.provider: "deepinfra"` |
|
||||
| Surface | Default model | OpenClaw config/tool |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
|
||||
| Chat / model provider | first chat-tagged entry from live catalog (static fallback `deepseek-ai/DeepSeek-V4-Flash`) | `agents.defaults.model` |
|
||||
| Image generation/editing | first `image-gen`-tagged entry from live catalog (static fallback `black-forest-labs/FLUX-1-schnell`) | `image_generate`, `agents.defaults.mediaModels.image` |
|
||||
| Media understanding | `moonshotai/Kimi-K2.5` for images | inbound image understanding |
|
||||
| Speech-to-text | `openai/whisper-large-v3-turbo` | inbound audio transcription |
|
||||
| Text-to-speech | `hexgrad/Kokoro-82M` | `tts.provider: "deepinfra"` |
|
||||
| Video generation | static fallback `Pixverse/Pixverse-T2V` (no live video-gen rows from DeepInfra today) | `video_generate`, `agents.defaults.mediaModels.video` |
|
||||
| Memory embeddings | `BAAI/bge-m3` | `memory.search.provider: "deepinfra"` |
|
||||
|
||||
DeepInfra also exposes reranking, classification, object-detection, and other
|
||||
native model types. OpenClaw has no provider contract for those categories
|
||||
|
||||
@@ -90,7 +90,7 @@ The default model onboarding writes is `litellm/claude-opus-4-6`.
|
||||
|
||||
LiteLLM can back the `image_generate` tool through OpenAI-compatible `/images/generations` and
|
||||
`/images/edits` routes. Default image model is `gpt-image-2`; configure a different one under
|
||||
`agents.defaults.imageGenerationModel`:
|
||||
`agents.defaults.mediaModels.image`:
|
||||
|
||||
```json5
|
||||
{
|
||||
|
||||
@@ -97,7 +97,7 @@ dynamically against OpenRouter's live model catalog.
|
||||
## Image generation
|
||||
|
||||
OpenRouter can back the `image_generate` tool. Set an OpenRouter image model
|
||||
under `agents.defaults.imageGenerationModel`:
|
||||
under `agents.defaults.mediaModels.image`:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -116,14 +116,14 @@ under `agents.defaults.imageGenerationModel`:
|
||||
OpenClaw sends image requests to OpenRouter's chat-completions image API with
|
||||
`modalities: ["image", "text"]`. Gemini image models additionally receive
|
||||
`aspectRatio` and `resolution` hints through OpenRouter's `image_config`; other
|
||||
image models do not. Use `agents.defaults.imageGenerationModel.timeoutMs` for
|
||||
image models do not. Use `agents.defaults.mediaModels.image.timeoutMs` for
|
||||
slower models; the `image_generate` tool's per-call `timeoutMs` still wins.
|
||||
|
||||
## Video generation
|
||||
|
||||
OpenRouter can back the `video_generate` tool through its asynchronous
|
||||
`/videos` API. Set an OpenRouter video model under
|
||||
`agents.defaults.videoGenerationModel`:
|
||||
`agents.defaults.mediaModels.video`:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -151,7 +151,7 @@ references.
|
||||
|
||||
OpenRouter can back the `music_generate` tool through chat-completions audio
|
||||
output. Set an OpenRouter audio model under
|
||||
`agents.defaults.musicGenerationModel`:
|
||||
`agents.defaults.mediaModels.music`:
|
||||
|
||||
```json5
|
||||
{
|
||||
|
||||
@@ -39,13 +39,13 @@ OpenClaw provides `pixverse` as an official external plugin for hosted PixVerse
|
||||
Non-interactive runs (key from `--pixverse-api-key` or `PIXVERSE_API_KEY`)
|
||||
default to International.
|
||||
|
||||
Onboarding also sets `agents.defaults.videoGenerationModel.primary` to
|
||||
Onboarding also sets `agents.defaults.mediaModels.video.primary` to
|
||||
`pixverse/v6` when no default video model is configured yet.
|
||||
|
||||
</Step>
|
||||
<Step title="Switch an existing default video provider (optional)">
|
||||
```bash
|
||||
openclaw config set agents.defaults.videoGenerationModel.primary "pixverse/v6"
|
||||
openclaw config set agents.defaults.mediaModels.video.primary "pixverse/v6"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Generate a video">
|
||||
@@ -154,7 +154,7 @@ The video provider accepts these optional provider-specific keys:
|
||||
PixVerse returns a `video_id` from the generation request. OpenClaw polls
|
||||
`/openapi/v2/video/result/{video_id}` every 5 seconds until the task
|
||||
succeeds, fails, or hits the timeout (default 5 minutes; override with
|
||||
`agents.defaults.videoGenerationModel.timeoutMs`).
|
||||
`agents.defaults.mediaModels.video.timeoutMs`).
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ OpenClaw ships a bundled `runway` provider for hosted video generation, enabled
|
||||
</Step>
|
||||
<Step title="Set Runway as the default video provider">
|
||||
```bash
|
||||
openclaw config set agents.defaults.videoGenerationModel.primary "runway/gen4.5"
|
||||
openclaw config set agents.defaults.mediaModels.video.primary "runway/gen4.5"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Generate a video">
|
||||
|
||||
@@ -244,7 +244,7 @@ stale context metadata on active 4.20 rows. It does not pin active 4.20
|
||||
- Video edit/extend inherit the input video's aspect ratio and resolution;
|
||||
those operations do not accept geometry overrides
|
||||
- Default operation timeout: 600 seconds unless `video_generate.timeoutMs`
|
||||
or `agents.defaults.videoGenerationModel.timeoutMs` is set
|
||||
or `agents.defaults.mediaModels.video.timeoutMs` is set
|
||||
|
||||
<Warning>
|
||||
Local video buffers are not accepted. Use remote `http(s)` URLs for video
|
||||
@@ -290,7 +290,7 @@ stale context metadata on active 4.20 rows. It does not pin active 4.20
|
||||
- Resolutions: `1K`, `2K`
|
||||
- Count: up to 4 images
|
||||
- Default operation timeout: 600 seconds unless `image_generate.timeoutMs`
|
||||
or `agents.defaults.imageGenerationModel.timeoutMs` is set
|
||||
or `agents.defaults.mediaModels.image.timeoutMs` is set
|
||||
|
||||
OpenClaw asks xAI for `b64_json` image responses so generated media can be
|
||||
stored and delivered through the normal channel attachment path. Local
|
||||
|
||||
@@ -65,7 +65,7 @@ Inbound media can be summarized or transcribed via a provider API before the rep
|
||||
|
||||
### Image and video generation
|
||||
|
||||
`image_generate` and `video_generate` route to whichever configured provider is available. Image generation can infer an auth-backed provider default when `agents.defaults.imageGenerationModel` is unset; video generation requires an explicit `agents.defaults.videoGenerationModel` (for example `qwen/wan2.6-t2v`).
|
||||
`image_generate` and `video_generate` route to whichever configured provider is available. Image generation can infer an auth-backed provider default when `agents.defaults.mediaModels.image` is unset; video generation requires an explicit `agents.defaults.mediaModels.video` (for example `qwen/wan2.6-t2v`).
|
||||
|
||||
See [Image generation](/tools/image-generation) and [Video generation](/tools/video-generation) for the current provider list.
|
||||
|
||||
|
||||
@@ -1315,7 +1315,7 @@ Credential resolution and redaction now cover more model and plugin paths, while
|
||||
- Windows exact-path execution approvals now launch the approved executable rather than a same-named program from the working directory. [#98260](https://github.com/openclaw/openclaw/pull/98260) Thanks @eleqtrizit.
|
||||
- `openclaw security audit` now warns that per-agent skill allowlists do not prevent shell-capable agents from reaching globally configured MCP servers. [#98352](https://github.com/openclaw/openclaw/pull/98352) Thanks @momothemage.
|
||||
- The Codex plugin now names per-action destructive approval mode `ask`, making it clear that each write or destructive action remains available but requires confirmation. [#98501](https://github.com/openclaw/openclaw/pull/98501) Related [#98499](https://github.com/openclaw/openclaw/issues/98499). Thanks @kevinslin.
|
||||
- Managed deployments can now require entries such as `system.run` in `gateway.nodes.denyCommands`, and Policy doctor warns when configuration drift leaves a privileged node command available. [#99121](https://github.com/openclaw/openclaw/pull/99121) Thanks @giodl73-repo.
|
||||
- Managed deployments can now require entries such as `system.run` in `gateway.nodes.commands.deny`, and Policy doctor warns when configuration drift leaves a privileged node command available. [#99121](https://github.com/openclaw/openclaw/pull/99121) Thanks @giodl73-repo.
|
||||
- Managed host tools no longer inherit the active Conda environment from the shell that launched OpenClaw, avoiding unintended Python and package-manager behavior. [#99425](https://github.com/openclaw/openclaw/pull/99425) Related [#99424](https://github.com/openclaw/openclaw/issues/99424). Thanks @krissding, @ooiuuii.
|
||||
- Long Discord approval previews now preserve complete emoji and flags when shortened, avoiding broken replacement characters. [#99539](https://github.com/openclaw/openclaw/pull/99539) Thanks @zhangguiping-xydt.
|
||||
- Exec approval prompts now display and transmit long command text containing emoji or malformed Unicode without broken characters or encoding failures. [#99566](https://github.com/openclaw/openclaw/pull/99566) Thanks @mikasa0818.
|
||||
@@ -1323,7 +1323,7 @@ Credential resolution and redaction now cover more model and plugin paths, while
|
||||
- Operators who explicitly enable Policy workspace repairs can now automatically narrow several unsafe settings, while OpenClaw remains read-only without that opt-in. [#99690](https://github.com/openclaw/openclaw/pull/99690) Thanks @giodl73-repo, @omarshahine.
|
||||
- Policy workspace repairs can now add required tools to the affected deny list while preserving existing entries and avoiding unintended root-policy widening. [#99700](https://github.com/openclaw/openclaw/pull/99700) Thanks @giodl73-repo.
|
||||
- Policy workspace repairs can now move reported open channel groups to allowlist mode and require mentions without changing inherited defaults. [#99720](https://github.com/openclaw/openclaw/pull/99720) Thanks @giodl73-repo, @omarshahine.
|
||||
- `doctor --fix` now shows the exact `gateway.bind=loopback` or `gateway.nodes.denyCommands` change needed for sensitive findings without applying it automatically. [#99776](https://github.com/openclaw/openclaw/pull/99776) Thanks @giodl73-repo.
|
||||
- `doctor --fix` now shows the exact `gateway.bind=loopback` or `gateway.nodes.commands.deny` change needed for sensitive findings without applying it automatically. [#99776](https://github.com/openclaw/openclaw/pull/99776) Thanks @giodl73-repo.
|
||||
- The Control UI terminal is now explicit opt-in, opens tabs under the selected agent's workspace and policy, and closes affected shells when accepted access restrictions tighten. [#100081](https://github.com/openclaw/openclaw/pull/100081) Thanks @rayncc.
|
||||
- Repeated pairing repair attempts now produce one usable macOS approval prompt per device instead of stacking stale alerts that cannot complete approval. [#100976](https://github.com/openclaw/openclaw/pull/100976)
|
||||
- Windows companion-node exec policies can now be viewed and updated through node-aware approvals commands and displayed correctly in Control UI. [#101669](https://github.com/openclaw/openclaw/pull/101669) Thanks @vincentkoc.
|
||||
@@ -2016,7 +2016,7 @@ Agents can read scanned PDFs with vision-capable models, work in isolated coding
|
||||
- Malformed browser fill data supplied through `--fields` or `--fields-file` now produces a clear actionable error and exits before any browser action starts. [#98861](https://github.com/openclaw/openclaw/pull/98861) Thanks @lsr911.
|
||||
- Running `/new` or `/reset` in a direct message now closes that conversation's managed browser tabs while leaving other people's tabs alone, even through a brief browser connection hiccup. [#100792](https://github.com/openclaw/openclaw/pull/100792) Thanks @fmls.
|
||||
- Agents can now screenshot a selected background Chromium tab without waiting for the browser command to time out, though capture may bring that tab forward. [#100857](https://github.com/openclaw/openclaw/pull/100857) Thanks @spencer2211.
|
||||
- Managed local browser pages can navigate normally under a restrictive `browser.ssrfPolicy.hostnameAllowlist`, while remote CDP endpoints and disallowed destinations remain blocked. [#100986](https://github.com/openclaw/openclaw/pull/100986) Thanks @nianjiuzst, @sarinv.
|
||||
- Managed local browser pages can navigate normally under a restrictive `browser.ssrfPolicy.allowedHostnames`, while remote CDP endpoints and disallowed destinations remain blocked. [#100986](https://github.com/openclaw/openclaw/pull/100986) Thanks @nianjiuzst, @sarinv.
|
||||
- Browser screenshots used privately for visual inspection are no longer attached automatically to channel replies and are shared only when explicitly requested. [#101434](https://github.com/openclaw/openclaw/pull/101434)
|
||||
- Browser automation attached to an existing Chrome session now stops with a bounded error when Chrome crashes or a request is cancelled, and later requests can reconnect cleanly. [#101454](https://github.com/openclaw/openclaw/pull/101454) Thanks @aniruddhaadak80.
|
||||
- Browser uploads on re-rendering pages now recover more reliably and expose the real stale-reference or action error instead of a generic timeout. [#101465](https://github.com/openclaw/openclaw/pull/101465) Thanks @diwakarrankawat, @m13v, @tigicion.
|
||||
|
||||
+4
-4
@@ -32,7 +32,7 @@ Auto-background the command after this delay (ms).
|
||||
Background the command immediately instead of waiting for `yieldMs`.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="timeout" type="number" default="tools.exec.timeoutSec">
|
||||
<ParamField path="timeout" type="number" default="tools.exec.timeoutSeconds">
|
||||
Override the configured exec timeout for this call, in seconds. Applies to foreground, background, `yieldMs`, gateway, sandbox, and node `system.run` execution. `timeout: 0` disables the exec process timeout for that call.
|
||||
</ParamField>
|
||||
|
||||
@@ -86,7 +86,7 @@ Notes:
|
||||
|
||||
| Key | Default | Notes |
|
||||
| ------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `tools.exec.timeoutSec` | `1800` | Default per-command exec timeout in seconds. Per-call `timeout` overrides it; per-call `timeout: 0` disables the exec process timeout. |
|
||||
| `tools.exec.timeoutSeconds` | `1800` | Default per-command exec timeout in seconds. Per-call `timeout` overrides it; per-call `timeout: 0` disables the exec process timeout. |
|
||||
| `tools.exec.host` | `auto` | Resolves to `sandbox` when a sandbox runtime is active, `gateway` otherwise. |
|
||||
| `tools.exec.security` | `deny` for sandbox, `full` for gateway/node when unset | |
|
||||
| `tools.exec.ask` | `off` | |
|
||||
@@ -148,11 +148,11 @@ When `tools.exec.strictInlineEval` is `true`, inline interpreter-eval forms requ
|
||||
- `host=sandbox`: runs `sh -lc` (login shell) inside the container, so `/etc/profile` may reset `PATH`. OpenClaw prepends `env.PATH` after profile sourcing via an internal env var (no shell interpolation); `tools.exec.pathPrepend` applies here too.
|
||||
- `host=node`: only non-blocked env overrides you pass are sent to the node. `env.PATH` overrides are rejected for host execution and ignored by node hosts. If you need additional PATH entries on a node, configure the node host service environment (systemd/launchd) or install tools in standard locations.
|
||||
|
||||
Per-agent node binding (use the agent list index in config):
|
||||
Per-agent node binding (use the keyed agent ID in config):
|
||||
|
||||
```bash
|
||||
openclaw config get agents.entries
|
||||
openclaw config set 'agents.entries[0].tools.exec.node' "node-id-or-name"
|
||||
openclaw config set 'agents.entries.main.tools.exec.node' "node-id-or-name"
|
||||
```
|
||||
|
||||
Control UI: the **Devices** page includes a small "Exec node binding" panel for the same settings.
|
||||
|
||||
@@ -21,7 +21,7 @@ lost.
|
||||
<Note>
|
||||
The tool only appears when at least one image-generation provider is
|
||||
available. If you do not see `image_generate` in your agent's tools,
|
||||
configure `agents.defaults.imageGenerationModel`, set up a provider API key,
|
||||
configure `agents.defaults.mediaModels.image`, set up a provider API key,
|
||||
or sign in with OpenAI ChatGPT/Codex OAuth.
|
||||
</Note>
|
||||
|
||||
@@ -240,12 +240,11 @@ from each attempt.
|
||||
</Accordion>
|
||||
<Accordion title="Auto-detection is auth-aware">
|
||||
A provider default only enters the candidate list when OpenClaw can
|
||||
actually authenticate that provider. Set
|
||||
`agents.defaults.mediaGenerationAutoProviderFallback: false` to use only
|
||||
explicit `model`, `primary`, and `fallbacks` entries.
|
||||
actually authenticate that provider. Automatic fallback across authenticated
|
||||
providers is always enabled; a per-call `model` remains authoritative.
|
||||
</Accordion>
|
||||
<Accordion title="Timeouts">
|
||||
Set `agents.defaults.imageGenerationModel.timeoutMs` for slow image
|
||||
Set `agents.defaults.mediaModels.image.timeoutMs` for slow image
|
||||
backends. A per-call `timeoutMs` tool parameter overrides the configured
|
||||
default, and configured defaults override plugin-authored provider
|
||||
defaults. Google and OpenRouter hosted image providers use 180 second
|
||||
|
||||
@@ -14,7 +14,7 @@ OpenRouter.
|
||||
|
||||
<Note>
|
||||
`music_generate` only appears when at least one music-generation provider is
|
||||
available: an explicit `agents.defaults.musicGenerationModel` config, or an
|
||||
available: an explicit `agents.defaults.mediaModels.music` config, or an
|
||||
auth-configured provider (a set API key, for example).
|
||||
</Note>
|
||||
|
||||
@@ -182,7 +182,7 @@ captures any requested-to-applied mapping.
|
||||
</Note>
|
||||
|
||||
Provider request timeouts are operator configuration only. OpenClaw uses
|
||||
`agents.defaults.musicGenerationModel.timeoutMs` when configured, raises
|
||||
`agents.defaults.mediaModels.music.timeoutMs` when configured, raises
|
||||
values below 120000ms to 120000ms, and otherwise defaults provider requests
|
||||
to 300000ms.
|
||||
|
||||
@@ -263,8 +263,8 @@ OpenClaw tries providers in this order:
|
||||
If a provider fails, the next candidate is tried automatically. If all
|
||||
fail, the error includes details from each attempt.
|
||||
|
||||
Set `agents.defaults.mediaGenerationAutoProviderFallback: false` to use only
|
||||
explicit `model`, `primary`, and `fallbacks` entries.
|
||||
Automatic fallback across authenticated providers is always enabled. A per-call
|
||||
`model` remains authoritative.
|
||||
|
||||
## Provider notes
|
||||
|
||||
|
||||
+6
-6
@@ -47,7 +47,7 @@ Optional model override in `provider/model` form.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="maxBytesMb" type="number">
|
||||
Per-PDF size cap in MB. Defaults to `agents.defaults.pdfMaxBytesMb`, or `10` if unset.
|
||||
Per-PDF size cap in MB. Defaults to `agents.defaults.pdfMaxMb`, or `10` if unset.
|
||||
</ParamField>
|
||||
|
||||
Notes:
|
||||
@@ -107,11 +107,11 @@ Details:
|
||||
}
|
||||
```
|
||||
|
||||
| Key | Default | Meaning |
|
||||
| ------------------------------- | ------- | ----------------------------------------------------------------------------------------- |
|
||||
| `agents.defaults.pdfModel` | unset | Explicit primary/fallback PDF models; falls back to `imageModel`, then the session model. |
|
||||
| `agents.defaults.pdfMaxBytesMb` | `10` | Per-PDF size cap in MB. |
|
||||
| `agents.defaults.pdfMaxPages` | `20` | Max pages processed per PDF. |
|
||||
| Key | Default | Meaning |
|
||||
| ----------------------------- | ------- | ----------------------------------------------------------------------------------------- |
|
||||
| `agents.defaults.pdfModel` | unset | Explicit primary/fallback PDF models; falls back to `imageModel`, then the session model. |
|
||||
| `agents.defaults.pdfMaxMb` | `10` | Per-PDF size cap in MB. |
|
||||
| `agents.defaults.pdfMaxPages` | `20` | Max pages processed per PDF. |
|
||||
|
||||
See [Configuration Reference](/gateway/config-agents#agent-defaults) for full field details.
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ Most skills configuration lives under `skills` in
|
||||
```
|
||||
|
||||
<Note>
|
||||
For built-in image generation, use `agents.defaults.imageGenerationModel`
|
||||
For built-in image generation, use `agents.defaults.mediaModels.image`
|
||||
plus the core `image_generate` tool instead of `skills.entries`. Skill
|
||||
entries are for custom or third-party skill workflows only.
|
||||
</Note>
|
||||
|
||||
@@ -16,7 +16,7 @@ available API keys.
|
||||
<Note>
|
||||
`video_generate` only appears when at least one video-generation provider is
|
||||
available. If it is missing from your agent tools, set a provider API key or
|
||||
configure `agents.defaults.videoGenerationModel`.
|
||||
configure `agents.defaults.mediaModels.video`.
|
||||
</Note>
|
||||
|
||||
`video_generate` has three runtime modes, resolved from the reference inputs
|
||||
@@ -42,7 +42,7 @@ active mode before submission and reports supported modes in `action=list`.
|
||||
</Step>
|
||||
<Step title="Pick a default model (optional)">
|
||||
```bash
|
||||
openclaw config set agents.defaults.videoGenerationModel.primary "google/veo-3.1-fast-generate-preview"
|
||||
openclaw config set agents.defaults.mediaModels.video.primary "google/veo-3.1-fast-generate-preview"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Ask the agent">
|
||||
@@ -219,7 +219,7 @@ dimensions). Providers that do not declare it surface the value via
|
||||
</ParamField>
|
||||
<ParamField path="model" type="string">Provider/model override (e.g. `runway/gen4.5`).</ParamField>
|
||||
<ParamField path="filename" type="string">Output filename hint.</ParamField>
|
||||
<ParamField path="timeoutMs" type="number">Optional provider operation timeout in milliseconds. When omitted, OpenClaw uses `agents.defaults.videoGenerationModel.timeoutMs` if configured, otherwise the plugin-authored provider default when one exists.</ParamField>
|
||||
<ParamField path="timeoutMs" type="number">Optional provider operation timeout in milliseconds. When omitted, OpenClaw uses `agents.defaults.mediaModels.video.timeoutMs` if configured, otherwise the plugin-authored provider default when one exists.</ParamField>
|
||||
<ParamField path="providerOptions" type="object">
|
||||
Provider-specific options as a JSON object (e.g. `{"seed": 42, "draft": true}`).
|
||||
Providers that declare a typed schema validate the keys and types; unknown
|
||||
@@ -298,8 +298,8 @@ OpenClaw resolves the model in this order:
|
||||
If a provider fails, the next candidate is tried automatically. If all
|
||||
candidates fail, the error includes details from each attempt.
|
||||
|
||||
Set `agents.defaults.mediaGenerationAutoProviderFallback: false` to use
|
||||
only the explicit `model`, `primary`, and `fallbacks` entries.
|
||||
Automatic fallback across authenticated providers is always enabled. A per-call
|
||||
`model` remains authoritative.
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -536,7 +536,7 @@ Set the default video-generation model in your OpenClaw config:
|
||||
Or via the CLI:
|
||||
|
||||
```bash
|
||||
openclaw config set agents.defaults.videoGenerationModel.primary "qwen/wan2.6-t2v"
|
||||
openclaw config set agents.defaults.mediaModels.video.primary "qwen/wan2.6-t2v"
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
@@ -532,7 +532,7 @@ Absolute external `http(s)` embed URLs stay blocked by default. To let `[embed u
|
||||
|
||||
## Chat message width
|
||||
|
||||
The chat transcript uses a centered readable frame aligned with the composer. Assistant and tool output stay left-aligned while user bubbles stay right-aligned inside that frame. Wide-monitor deployments can override the transcript width without patching bundled CSS by setting `gateway.controlUi.chatMessageMaxWidth`:
|
||||
The chat transcript uses a centered readable frame aligned with the composer. Assistant and tool output stay left-aligned while user bubbles stay right-aligned inside that frame. Wide-monitor deployments can override the transcript width without patching bundled CSS by setting `ui.prefs.chatMessageMaxWidth`:
|
||||
|
||||
```json5
|
||||
{
|
||||
|
||||
@@ -85,9 +85,9 @@ function setSlackSetupIdentity(params: {
|
||||
if (slack.postAs === "user") {
|
||||
// Named accounts inherit the root identity, so an explicit bot value is
|
||||
// required only when overriding a user-identity channel default.
|
||||
nextAccount.identity = "bot";
|
||||
nextAccount.postAs = "bot";
|
||||
} else {
|
||||
delete nextAccount.identity;
|
||||
delete nextAccount.postAs;
|
||||
}
|
||||
return {
|
||||
...next,
|
||||
|
||||
@@ -45,14 +45,10 @@ function createConfig(
|
||||
extras?: Partial<NonNullable<OpenClawConfig["channels"]>["whatsapp"]>,
|
||||
): OpenClawConfig {
|
||||
return {
|
||||
messages: { ackReaction: "👀", ackReactionScope: "all" },
|
||||
channels: {
|
||||
whatsapp: {
|
||||
reactionLevel,
|
||||
ackReaction: {
|
||||
emoji: "👀",
|
||||
direct: true,
|
||||
group: "mentions",
|
||||
},
|
||||
...extras,
|
||||
},
|
||||
},
|
||||
@@ -137,18 +133,15 @@ describe("maybeSendAckReaction", () => {
|
||||
expectAckReactionSent("work", cfg);
|
||||
});
|
||||
|
||||
it("uses the agent identity emoji when WhatsApp ackReaction has no emoji", async () => {
|
||||
it("uses the canonical emoji preserved from agent identity", async () => {
|
||||
const cfg = {
|
||||
agents: {
|
||||
list: [{ id: "agent", identity: { emoji: "🔥" } }],
|
||||
entries: { agent: { identity: { emoji: "🔥" } } },
|
||||
},
|
||||
messages: { ackReaction: "🔥", ackReactionScope: "all" },
|
||||
channels: {
|
||||
whatsapp: {
|
||||
reactionLevel: "ack",
|
||||
ackReaction: {
|
||||
direct: true,
|
||||
group: "mentions",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
@@ -190,13 +183,7 @@ describe("maybeSendAckReaction", () => {
|
||||
});
|
||||
|
||||
it("uses the sender LID as the group reaction participant when no sender JID is available", async () => {
|
||||
const cfg = createConfig("ack", {
|
||||
ackReaction: {
|
||||
emoji: "👀",
|
||||
direct: true,
|
||||
group: "always",
|
||||
},
|
||||
});
|
||||
const cfg = createConfig("ack");
|
||||
const ackReaction = await runAckReaction({
|
||||
cfg,
|
||||
msg: createMessage({
|
||||
|
||||
@@ -41,12 +41,15 @@ export async function maybeSendAckReaction(params: {
|
||||
}
|
||||
|
||||
const ackConfig = params.cfg.messages?.ackReaction;
|
||||
const scope = params.cfg.messages?.ackReactionScope ?? "group-mentions";
|
||||
if (scope === "off" || scope === "none") {
|
||||
return null;
|
||||
}
|
||||
const emoji = resolveWhatsAppAckEmoji({
|
||||
cfg: params.cfg,
|
||||
agentId: params.agentId,
|
||||
ackConfig,
|
||||
});
|
||||
const scope = params.cfg.messages?.ackReactionScope ?? "group-mentions";
|
||||
const directEnabled = scope === "all" || scope === "direct";
|
||||
const groupMode =
|
||||
scope === "all" || scope === "group-all" ? "always" : scope === "direct" ? "never" : "mentions";
|
||||
|
||||
@@ -48,6 +48,8 @@ describe("createWhatsAppStatusReactionController", () => {
|
||||
it("uses the sender LID as the group reaction participant when no sender JID is available", async () => {
|
||||
const cfg = {
|
||||
messages: {
|
||||
ackReaction: "👀",
|
||||
ackReactionScope: "all",
|
||||
statusReactions: {
|
||||
enabled: true,
|
||||
},
|
||||
@@ -55,11 +57,6 @@ describe("createWhatsAppStatusReactionController", () => {
|
||||
channels: {
|
||||
whatsapp: {
|
||||
reactionLevel: "ack",
|
||||
ackReaction: {
|
||||
emoji: "👀",
|
||||
direct: true,
|
||||
group: "always",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
@@ -121,12 +118,14 @@ describe("createWhatsAppStatusReactionController", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("uses the agent identity emoji when WhatsApp ackReaction has no emoji", async () => {
|
||||
it("uses the canonical emoji preserved from agent identity", async () => {
|
||||
const cfg = {
|
||||
agents: {
|
||||
list: [{ id: "agent", identity: { emoji: "🔥" } }],
|
||||
entries: { agent: { identity: { emoji: "🔥" } } },
|
||||
},
|
||||
messages: {
|
||||
ackReaction: "🔥",
|
||||
ackReactionScope: "all",
|
||||
statusReactions: {
|
||||
enabled: true,
|
||||
},
|
||||
@@ -134,10 +133,6 @@ describe("createWhatsAppStatusReactionController", () => {
|
||||
channels: {
|
||||
whatsapp: {
|
||||
reactionLevel: "ack",
|
||||
ackReaction: {
|
||||
direct: true,
|
||||
group: "mentions",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
@@ -170,6 +165,8 @@ describe("createWhatsAppStatusReactionController", () => {
|
||||
it("uses the active account reactionLevel override from admission", async () => {
|
||||
const cfg = {
|
||||
messages: {
|
||||
ackReaction: "👀",
|
||||
ackReactionScope: "all",
|
||||
statusReactions: {
|
||||
enabled: true,
|
||||
},
|
||||
@@ -177,11 +174,6 @@ describe("createWhatsAppStatusReactionController", () => {
|
||||
channels: {
|
||||
whatsapp: {
|
||||
reactionLevel: "off",
|
||||
ackReaction: {
|
||||
emoji: "👀",
|
||||
direct: true,
|
||||
group: "mentions",
|
||||
},
|
||||
accounts: {
|
||||
work: {
|
||||
reactionLevel: "ack",
|
||||
|
||||
@@ -47,6 +47,10 @@ export async function createWhatsAppStatusReactionController(
|
||||
}
|
||||
|
||||
const ackConfig = params.cfg.messages?.ackReaction;
|
||||
const scope = params.cfg.messages?.ackReactionScope ?? "group-mentions";
|
||||
if (scope === "off" || scope === "none") {
|
||||
return null;
|
||||
}
|
||||
const ackEmoji = resolveWhatsAppAckEmoji({
|
||||
cfg: params.cfg,
|
||||
agentId: params.agentId,
|
||||
@@ -55,7 +59,6 @@ export async function createWhatsAppStatusReactionController(
|
||||
if (!ackEmoji) {
|
||||
return null;
|
||||
}
|
||||
const scope = params.cfg.messages?.ackReactionScope ?? "group-mentions";
|
||||
const directEnabled = scope === "all" || scope === "direct";
|
||||
const groupMode =
|
||||
scope === "all" || scope === "group-all" ? "always" : scope === "direct" ? "never" : "mentions";
|
||||
|
||||
@@ -22,6 +22,13 @@ describe("legacy config migration end to end", () => {
|
||||
agents: { entries: { main: { name: "canonical" } }, list: [{ id: "main", name: "old" }] },
|
||||
});
|
||||
expect(canonicalWins.next).toEqual({ agents: { entries: { main: { name: "canonical" } } } });
|
||||
|
||||
const prototypeId = applyLegacyDoctorMigrations({
|
||||
agents: { list: [{ id: "__proto__", name: "prototype-safe" }] },
|
||||
});
|
||||
const prototypeEntries = (prototypeId.next?.agents as { entries?: Record<string, unknown> })
|
||||
?.entries;
|
||||
expect(Object.hasOwn(prototypeEntries ?? {}, "__proto__")).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps agents.defaults.tts outside the schema", () => {
|
||||
|
||||
@@ -29,7 +29,12 @@ function migrateAgentEntries(raw: Record<string, unknown>, changes: string[]): v
|
||||
suffix += 1;
|
||||
}
|
||||
const { id: _id, ...config } = entry;
|
||||
entries[key] = config;
|
||||
Object.defineProperty(entries, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value: config,
|
||||
writable: true,
|
||||
});
|
||||
if (key !== requestedId) {
|
||||
changes.push(`Moved duplicate agents.list id "${requestedId}" to agents.entries.${key}.`);
|
||||
}
|
||||
|
||||
@@ -717,8 +717,22 @@ function migrateFinalLayoutKills(raw: Record<string, unknown>, changes: string[]
|
||||
return;
|
||||
}
|
||||
messages ??= ensureRecord(raw, "messages");
|
||||
if (messages.ackReaction === undefined && typeof ack.emoji === "string") {
|
||||
messages.ackReaction = ack.emoji;
|
||||
if (messages.ackReaction === undefined) {
|
||||
const legacyAgents = getRecord(raw.agents)?.list;
|
||||
const agentEntries = Array.isArray(legacyAgents)
|
||||
? legacyAgents.filter((value): value is Record<string, unknown> =>
|
||||
Boolean(getRecord(value)),
|
||||
)
|
||||
: [];
|
||||
const defaultAgent =
|
||||
agentEntries.find((value) => getRecord(value)?.default === true) ?? agentEntries[0];
|
||||
const identityEmoji = getRecord(getRecord(defaultAgent)?.identity)?.emoji;
|
||||
messages.ackReaction =
|
||||
typeof ack.emoji === "string"
|
||||
? ack.emoji
|
||||
: typeof identityEmoji === "string"
|
||||
? identityEmoji
|
||||
: "👀";
|
||||
}
|
||||
if (messages.ackReactionScope === undefined) {
|
||||
const direct = ack.direct !== false;
|
||||
|
||||
@@ -223,6 +223,7 @@ export function resolveCapabilityModelCandidates(params: {
|
||||
return [override];
|
||||
}
|
||||
|
||||
// Cross-provider fallback is a fixed product policy; Doctor removes the retired opt-out.
|
||||
const autoProviderFallbackEnabled = params.autoProviderFallback ?? true;
|
||||
add(params.modelOverride, { useProviderMetadata: true });
|
||||
add(resolveAgentModelPrimaryValue(params.modelConfig), {
|
||||
|
||||
@@ -99,7 +99,7 @@ vi.mock("../infra/exec-approvals.js", () => ({
|
||||
vi.mock("../agents/agent-scope.js", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("../agents/agent-scope.js")>()),
|
||||
resolveAgentDir: (config: OpenClawConfig, agentId: string) =>
|
||||
config.agents?.list?.find((agent) => agent.id === agentId)?.agentDir ?? `/agents/${agentId}`,
|
||||
config.agents?.entries?.[agentId]?.agentDir ?? `/agents/${agentId}`,
|
||||
}));
|
||||
|
||||
import { applySystemAgentModelSelection, applySystemAgentSetup } from "./setup-apply.js";
|
||||
@@ -191,9 +191,8 @@ describe("applySystemAgentModelSelection", () => {
|
||||
"openai/gpt-5.5": { agentRuntime: { id: "codex" } },
|
||||
},
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "work",
|
||||
entries: {
|
||||
work: {
|
||||
default: true,
|
||||
model: "openai/gpt-5.5",
|
||||
models: {
|
||||
@@ -203,7 +202,7 @@ describe("applySystemAgentModelSelection", () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
} satisfies OpenClawConfig;
|
||||
|
||||
@@ -213,8 +212,8 @@ describe("applySystemAgentModelSelection", () => {
|
||||
});
|
||||
|
||||
expect(result.agents?.defaults?.models?.["openai/gpt-5.5"]?.agentRuntime).toBeUndefined();
|
||||
expect(result.agents?.list?.[0]?.models?.["openai/gpt-5.5"]).toEqual({ alias: "primary" });
|
||||
expect(result.agents?.list?.[0]?.model).toBe("openai/gpt-5.5");
|
||||
expect(result.agents?.entries?.work?.models?.["openai/gpt-5.5"]).toEqual({ alias: "primary" });
|
||||
expect(result.agents?.entries?.work?.model).toBe("openai/gpt-5.5");
|
||||
});
|
||||
|
||||
it("pins the verified credential without creating a global visibility map", async () => {
|
||||
@@ -345,7 +344,7 @@ describe("applySystemAgentSetup transaction boundaries", () => {
|
||||
const config = {
|
||||
agents: {
|
||||
defaults: { model: "openai/gpt-5.5" },
|
||||
list: [{ id: "OpenClaw" }],
|
||||
entries: { OpenClaw: {} },
|
||||
},
|
||||
} satisfies OpenClawConfig;
|
||||
mocks.state.initialSnapshot = snapshot("reserved", config);
|
||||
@@ -361,7 +360,7 @@ describe("applySystemAgentSetup transaction boundaries", () => {
|
||||
const config = {
|
||||
agents: {
|
||||
defaults: { model: "openai/gpt-5.5" },
|
||||
list: [{ id: "crestodian" }], // reserved retired id
|
||||
entries: { crestodian: {} }, // reserved retired id
|
||||
},
|
||||
} satisfies OpenClawConfig;
|
||||
mocks.state.initialSnapshot = snapshot("reserved-retired", config);
|
||||
@@ -389,7 +388,7 @@ describe("applySystemAgentSetup transaction boundaries", () => {
|
||||
runtimeConfig: {
|
||||
agents: {
|
||||
defaults: { model: { primary: "openai/gpt-5.5" } },
|
||||
list: [{ id: "other", default: true }],
|
||||
entries: { other: { default: true } },
|
||||
},
|
||||
},
|
||||
error: "default agent changed",
|
||||
@@ -418,12 +417,14 @@ describe("applySystemAgentSetup transaction boundaries", () => {
|
||||
});
|
||||
|
||||
it("rejects same-revision agent credential directory drift in the final snapshot", async () => {
|
||||
mocks.state.commitSnapshot = snapshot("probe", {
|
||||
const movedConfig: OpenClawConfig = {
|
||||
agents: {
|
||||
defaults: { model: { primary: "openai/gpt-5.5" } },
|
||||
list: [{ id: "main", default: true, agentDir: "/agents/moved" }],
|
||||
entries: { main: { default: true, agentDir: "/agents/moved" } },
|
||||
},
|
||||
});
|
||||
};
|
||||
mocks.state.commitConfig = movedConfig;
|
||||
mocks.state.commitSnapshot = snapshot("probe", movedConfig);
|
||||
|
||||
await expect(
|
||||
applySystemAgentSetup(
|
||||
|
||||
@@ -173,7 +173,11 @@ function applySystemAgentModelSelectionWithModules(
|
||||
}
|
||||
|
||||
nextConfig.agents.entries ??= {};
|
||||
let agent = nextConfig.agents.entries[agentId];
|
||||
const agentEntryKey =
|
||||
Object.keys(nextConfig.agents.entries).find(
|
||||
(entryId) => normalizeAgentId(entryId) === agentId,
|
||||
) ?? agentId;
|
||||
let agent = nextConfig.agents.entries[agentEntryKey];
|
||||
if (writesAgent) {
|
||||
if (!agent) {
|
||||
throw new Error(`Could not resolve configured default agent "${agentId}".`);
|
||||
@@ -186,7 +190,7 @@ function applySystemAgentModelSelectionWithModules(
|
||||
if (params.agentRuntimeId) {
|
||||
if (!agent) {
|
||||
agent = { default: true };
|
||||
nextConfig.agents.entries[agentId] = agent;
|
||||
nextConfig.agents.entries[agentEntryKey] = agent;
|
||||
}
|
||||
const agentModels = { ...agent.models };
|
||||
const agentKey = modelConfig.upsertCanonicalModelConfigEntry(agentModels, target);
|
||||
|
||||
@@ -16,7 +16,7 @@ export function requireValidSystemAgentSetupSnapshot(snapshot: ConfigFileSnapsho
|
||||
}
|
||||
const sourceConfig = snapshot.exists ? (snapshot.sourceConfig ?? snapshot.config) : {};
|
||||
const runtimeConfig = snapshot.exists ? (snapshot.runtimeConfig ?? snapshot.config) : {};
|
||||
const reservedAgent = runtimeConfig.agents?.list?.find((entry) =>
|
||||
const reservedAgent = listAgentEntries(runtimeConfig).find((entry) =>
|
||||
isReservedSystemAgentId(entry.id),
|
||||
);
|
||||
if (reservedAgent) {
|
||||
@@ -26,3 +26,4 @@ export function requireValidSystemAgentSetupSnapshot(snapshot: ConfigFileSnapsho
|
||||
}
|
||||
return { sourceConfig, runtimeConfig };
|
||||
}
|
||||
import { listAgentEntries } from "../agents/agent-scope.js";
|
||||
|
||||
Reference in New Issue
Block a user