mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
refactor(config): config-surface reduction tranche 3 — product consolidations (review request) (#111527)
* refactor(config): consolidate media model lists * refactor(config): unify memory configuration * refactor(config): consolidate TTS ownership * refactor(config): move typing policy to agents * refactor(config): retire product-level config surfaces * refactor(config): share scoped tool policy type * chore(config): refresh generated baselines * fix(config): honor agent typing overrides * fix(config): migrate sibling config consumers * refactor(infra): keep base64url decoder private * fix(config): strip invalid legacy TTS values * chore(config): refresh rebased baseline hash * fix(doctor): route legacy messages.tts.realtime voice to talk during tts move * refactor(config): polish final layout names * refactor(config): freeze retired tuning defaults * feat(config): add fast mode default symmetry * refactor(config): key agent entries by id * docs(config): update final layout reference * test(config): cover final layout migrations * chore(config): refresh final layout baselines * fix(config): align final layout runtime readers * fix(config): align remaining readers * fix(config): stabilize final layout migrations * fix(config): finalize config projection proof * fix(config): address final layout review * docs(release): preserve historical config names * fix(config): complete keyed agent migration * fix(config): close final migration gaps * fix(config): finish full-branch review * fix(config): complete runtime secret detection * fix(config): close final review findings * fix(config): finish canonical docs and heartbeat migration * fix(config): integrate latest main after rebase * refactor(env): isolate test-only controls * refactor(env): isolate build and development controls * refactor(env): collapse process identity indirection * refactor(env): remove duplicate config and temp aliases * docs(env): define the operator-facing allowlist * ci(env): ratchet production variable count * fix(env): remove stale provider helper import * fix(env): make ratchet sorting explicit * test(env): keep test seam in dead-code audit * test(env): cover ratchet growth and boundary; document surface budgets * docs(config): document tier-eval consolidations * docs(config): clarify speech preference ownership * test(memory): align retired tuning fixtures * refactor(memory): freeze engine heuristics * refactor(config): apply tier-eval tranche * refactor(tts): move persona shaping to providers * refactor(compaction): move prompt policy to providers * test(config): align hookified prompt fixtures * chore(deadcode): classify test-only exports * chore(github): remove unused spawn helper * chore(deadcode): classify queue diagnostics * chore(deadcode): remove unused lane snapshot export * chore(plugin-sdk): ratchet consolidated surface * fix(config): integrate latest main after rebase
This commit is contained in:
committed by
GitHub
parent
8ecb609990
commit
edecdbd05e
@@ -9,7 +9,7 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { spawnPlainGh } from "../../../../scripts/lib/plain-gh.mjs";
|
||||
import { execPlainGh } from "../../../../scripts/lib/plain-gh.mjs";
|
||||
|
||||
const REPO = "openclaw/openclaw";
|
||||
const REPO_URL = `https://github.com/${REPO}`;
|
||||
@@ -29,25 +29,30 @@ function tmpFile(purpose) {
|
||||
}
|
||||
|
||||
function gh(args, { json = true, allowFailure = false } = {}) {
|
||||
const proc = spawnPlainGh(args, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024 });
|
||||
if (proc.status !== 0 && !allowFailure) {
|
||||
fail(`gh ${args.slice(0, 3).join(" ")} failed:\n${(proc.stderr || proc.stdout || "").trim()}`);
|
||||
}
|
||||
if (proc.status !== 0) {
|
||||
return {
|
||||
let stdout;
|
||||
try {
|
||||
stdout = execPlainGh(args, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024 });
|
||||
} catch (error) {
|
||||
const failure = {
|
||||
gh_failed: true,
|
||||
status: proc.status,
|
||||
stdout: proc.stdout,
|
||||
stderr: proc.stderr,
|
||||
status: error?.status ?? 1,
|
||||
stdout: String(error?.stdout ?? ""),
|
||||
stderr: String(error?.stderr ?? ""),
|
||||
};
|
||||
if (!allowFailure) {
|
||||
fail(
|
||||
`gh ${args.slice(0, 3).join(" ")} failed:\n${(failure.stderr || failure.stdout).trim()}`,
|
||||
);
|
||||
}
|
||||
return failure;
|
||||
}
|
||||
if (!json) {
|
||||
return proc.stdout;
|
||||
return stdout;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(proc.stdout);
|
||||
return JSON.parse(stdout);
|
||||
} catch {
|
||||
return proc.stdout;
|
||||
return stdout;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Distinct OPENCLAW_* names in production source under src, packages, and extensions.
|
||||
# Ratchet: lower this number when cleanup removes names; never raise it.
|
||||
530
|
||||
@@ -357,6 +357,9 @@ const config = {
|
||||
"src/boards/board-layout.ts": ["types"],
|
||||
"src/boards/board-notices.ts": ["exports"],
|
||||
"src/boards/board-store.ts": ["exports"],
|
||||
// Test and E2E callers reach these hooks through runtime.test-support.ts;
|
||||
// the full-tree companion config still audits their actual consumers.
|
||||
"src/commitments/runtime.ts": ["exports"],
|
||||
"src/gateway/board-view-ticket.ts": ["exports"],
|
||||
// GatewayBoardProvider and boardExists are constructed/asserted by the
|
||||
// focused Control UI provider tests, not by a separate production module.
|
||||
@@ -364,6 +367,10 @@ const config = {
|
||||
// Greeting cache/fact contracts (hash, alert text, store shapes) are
|
||||
// asserted by the focused greeting unit tests, not by another prod module.
|
||||
"src/system-agent/greeting.ts": ["exports", "types"],
|
||||
// Focused tests consume these diagnostic/test seams; production code uses
|
||||
// the surrounding runtime helpers rather than importing the exports.
|
||||
"extensions/signal/src/setup-core.ts": ["exports"],
|
||||
"src/infra/heartbeat-wake.ts": ["exports"],
|
||||
},
|
||||
workspaces: {
|
||||
".": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"core": 2851,
|
||||
"channel": 3680,
|
||||
"plugin": 3537
|
||||
"core": 2351,
|
||||
"channel": 3627,
|
||||
"plugin": 3556
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
a8e315e49d62b95c54c752041cc27628260686f0a99de646c24505edc977a3c3 config-baseline.json
|
||||
d3da432d372bc652d432be978495a9f28b3478da1034cbe888550c8bf6cb9dd4 config-baseline.core.json
|
||||
dbf05e8852c873d48288336dbbb05d7a7b3a1bcbee8cb8ece5e40f204d2ab9db config-baseline.channel.json
|
||||
0e0592b95e4958477e539abd5ccb336c63735c7d69133199953c48ff3c0845a8 config-baseline.plugin.json
|
||||
efd6e8ac56ba46833230deef3552065b6e5588d4ac28efe856332e0e56eb2668 config-baseline.json
|
||||
dbf9485e8f4bb6ac112ef1a9cd71941abd00cd9593c8094dfa1c16517456017e config-baseline.core.json
|
||||
0ab331946d642d2e1ebf69033f0a79ffad53472ecccf4b7cc96211bea79f3788 config-baseline.channel.json
|
||||
fab3420186223207228066225cf4577702fdcbaf40ba0c35f8315d24019acbc4 config-baseline.plugin.json
|
||||
|
||||
@@ -36,7 +36,7 @@ c97dd36cdf8f83c2893c33e9430a93cd131a03d855725783ca5b545de0cf84f8 module/channel
|
||||
e2b4d1923a19b927e912622576d43d20a6e188c124a7bc588fe567cd3c1c924e module/channel-outbound
|
||||
50c61de5d522abadcae79841335aeac25e3791e6dbb0cfc3d64b2720a8c514ce module/channel-pairing
|
||||
ee4292b069d4d48cce4fc2dc26df5b5c87eb1fa4769f1f6be9a10c3e1221e1a9 module/channel-plugin-common
|
||||
bb82ca1819308189c4ba7b1e83e0c39fb0fb8b5a7081ee874480d64426778454 module/channel-policy
|
||||
01b95b0b8aca5005594969f0df221111a76be454b70102e4b452051c87b6c179 module/channel-policy
|
||||
cc0a77137b304b27a313791aa30e43efb4acc254da7590bd47493d81e7104fba module/channel-reply-pipeline
|
||||
bba5540be7cf9613a163663decdb2affe2af9bbd3ad7914989ab186f9c2abec1 module/channel-runtime-context
|
||||
17cec26bc71fc43a066049ef63f95bf29737113c26ab13689ceff602b9aa11d6 module/channel-secret-basic-runtime
|
||||
@@ -51,16 +51,16 @@ c89ec1b194b76f67a6f4dd108dccf460da6065646cba31374c8aa748f23a39e4 module/collect
|
||||
fa2df02bede6ed8843e5c2bd605c6ea5cd20313c305593fbd371dba6f1b931c3 module/command-detection
|
||||
28a0cae8dee664aef14b1a64c86a46d0fb5637cf9f671790d4fc9ba205e42820 module/command-primitives-runtime
|
||||
eb4c757fe0086c1dbfa4c3f3caf3dcff0d3cab3924c608237f08f740a6ee5f59 module/command-status
|
||||
235a9e4d983042c3db156efcab0e333984cbbf13ebdfcc44a3a5ab40cf3edb4f module/config-contracts
|
||||
0ef109bca0630cab8578b6d1ad1eb0f92e76dc3995f4a180e34f03212d42f1e0 module/config-contracts
|
||||
20f3f8042de53e4eee61b64de9102c8c202b9299e6a29235647a4729f70145f2 module/config-mutation
|
||||
6f23fd2d777f7189c38b34a51c6899abdad4ee4e6d74458eb3183c8ce03b8677 module/config-runtime
|
||||
189fa5a240cad0404cd281ad0a14a105a8f3231278d87b71cfbc4f96fb8e48ef module/config-runtime
|
||||
c1ea9510dfda047609a99d5d2cd1f1560f5d469a36e6b695766213d695c25b0f module/conversation-runtime
|
||||
d9267aacc65aeebf0046d4eef691e5a510764063e6c7942b1d6bc7a282ca67a1 module/core
|
||||
4af19d59c2f18674e7d7f7dc1b358b644dc707e6bd601dc47168bd9e4a669940 module/dedupe-runtime
|
||||
f70c93d28053ca2e8353e45e6515ce7acef188097c6117d1545965d0699c8004 module/device-bootstrap
|
||||
6215d3af5923bf5a616d73062534968b69f448e3e30adc64ae9caebdd1a46d71 module/diagnostic-runtime
|
||||
ea81ef06956c1bc0853fa00afbbc2b5a4019116aaf8a436e1b27d06f7a2c9e88 module/directory-runtime
|
||||
c7c9119cd43e2ed2eed4fe8bd57a6b75d2ee9a1f7926c432634c49b54ff7f9a6 module/discord
|
||||
2cf82472e3e7645fabc581b4531c91732374f6844969e763683401728f284c88 module/discord
|
||||
46c05a90b66032d1d7ad08445840a4bf81aa2bd325348f87710ad4538daf38f6 module/error-runtime
|
||||
b013053a61e7d9be3d0c683c02baf57fa7e4393ec54e0df6a46ab0f2fe2348fd module/extension-shared
|
||||
ceacad83db01c66e7be6aa21a291597020f13f737b697690eae7d47098e6499a module/gateway-method-runtime
|
||||
@@ -70,7 +70,7 @@ ceacad83db01c66e7be6aa21a291597020f13f737b697690eae7d47098e6499a module/gateway
|
||||
182dc685f2103ff66c1a4839a48f4f40d2eeb0070cd74e449b47aacc4e6f1c22 module/hook-runtime
|
||||
f6e3c44e7d1090a97aca554a3c247219b8de78b3cb4399cac5efde8a0a6c1156 module/inbound-envelope
|
||||
67142e8e63ea860a03b0cbb5660f63405a83ec575f2ef7a96f9b87f1be362808 module/inbound-reply-dispatch
|
||||
841de925321fbc69c8dcc1beed2468dfcd5f28cc38a8aad6fcde20d85158a985 module/infra-runtime
|
||||
e6abca6089332c08bdbb049a41d258bd0aedb0dacb5757b5833d67af67aa3117 module/infra-runtime
|
||||
2e717cccb3db127aed0287d4ea14c41a8e64e46d60c728638153e31e2fb0d296 module/ingress-effect-once
|
||||
74fee62a94618d830a5282a7816a73b85570b2e4ce17b90078078734510511d7 module/interactive-runtime
|
||||
9dd66baf2def46386ad4706380e57f9068fa9bb3878be2d9ba961ec2f46d3d87 module/json-store
|
||||
@@ -83,7 +83,7 @@ a5f59c9acbcaa3f82247bf806eb5ba08032373fb853719f0ec9457690f16fc70 module/media-m
|
||||
c5e3eb1a584f4b8126d9d6c177a840ec9103671e8d1242634ee67db9b5b9e573 module/media-understanding
|
||||
c0ffaed532578cf33493992e1ff806b2268b8e3774a92edbaede5cf5bda162a6 module/media-understanding-runtime
|
||||
075b7a2783cf22c3210cfce630b815c2e3b7e85c1f5606f0dde4730d47242365 module/meeting-runtime
|
||||
4205ab767b79e740c7c7137079b19b315c85f7f8c748592840f00bbf70a71709 module/memory-core-host-engine-foundation
|
||||
d09ddb38c9d20a41fcf4cedaf6a2c28eb5333a9fba2287e34b458fa5caba751c module/memory-core-host-engine-foundation
|
||||
646773d8282a2ac6a89101685c406200935ddb3459a2830e820da132fa433b3c module/memory-host-core
|
||||
87b7a3206346c0d4b294fb3a2395cbaabc3e73ff8b1b9ea925bc3aade3e52687 module/messaging-targets
|
||||
09f842a2787b87117d88ba6545aa0441ff8afdb85e2e66ae4d1838ebecb16148 module/model-session-runtime
|
||||
|
||||
@@ -3,14 +3,13 @@ doc-schema-version: 1
|
||||
summary: "Overview of automation mechanisms: tasks, cron, hooks, standing orders, and Task Flow"
|
||||
read_when:
|
||||
- Deciding how to automate work with OpenClaw
|
||||
- Choosing between heartbeat, cron, commitments, hooks, and standing orders
|
||||
- Choosing between heartbeat, cron, hooks, and standing orders
|
||||
- Looking for the right automation entry point
|
||||
title: "Automation"
|
||||
---
|
||||
|
||||
OpenClaw runs work in the background through tasks, scheduled jobs, inferred
|
||||
commitments, event hooks, and standing instructions. Use this page to pick the
|
||||
right mechanism.
|
||||
OpenClaw runs work in the background through tasks, scheduled jobs, event hooks,
|
||||
and standing instructions. Use this page to pick the right mechanism.
|
||||
|
||||
## Quick decision guide
|
||||
|
||||
@@ -21,7 +20,6 @@ flowchart TD
|
||||
START --> Q3{Orchestrate multi-step flows?}
|
||||
START --> Q4{React to lifecycle events?}
|
||||
START --> Q5{Give the agent persistent instructions?}
|
||||
START --> Q6{Remember a natural follow-up?}
|
||||
|
||||
Q1 -->|Yes| Q1a{Exact timing or flexible?}
|
||||
Q1a -->|Exact| CRON["Scheduled Tasks (Cron)"]
|
||||
@@ -31,7 +29,6 @@ flowchart TD
|
||||
Q3 -->|Yes| FLOW[Task Flow]
|
||||
Q4 -->|Yes| HOOKS[Hooks]
|
||||
Q5 -->|Yes| SO[Standing Orders]
|
||||
Q6 -->|Yes| COMMITMENTS[Inferred Commitments]
|
||||
```
|
||||
|
||||
| Use case | Recommended | Why |
|
||||
@@ -41,8 +38,6 @@ flowchart TD
|
||||
| Run weekly deep analysis | Scheduled Tasks (Cron) | Standalone task, can use different model |
|
||||
| Check inbox every 30 min | Heartbeat | Batches with other checks, context-aware |
|
||||
| Monitor calendar for upcoming events | Heartbeat | Natural fit for periodic awareness |
|
||||
| Check in after a mentioned interview | Inferred Commitments | Memory-like follow-up, no exact reminder request |
|
||||
| Gentle care check-in after user context | Inferred Commitments | Scoped to the same agent and channel |
|
||||
| Inspect status of a subagent or ACP run | Background Tasks | Tasks ledger tracks all detached work |
|
||||
| Audit what ran and when | Background Tasks | `openclaw tasks list` and `openclaw tasks audit` |
|
||||
| Multi-step research then summarize | Task Flow | Durable orchestration with revision tracking |
|
||||
@@ -76,15 +71,6 @@ The background task ledger tracks all detached work: ACP runs, subagent spawns,
|
||||
|
||||
See [Background Tasks](/automation/tasks).
|
||||
|
||||
### Inferred commitments
|
||||
|
||||
Commitments are opt-in, short-lived follow-up memories. OpenClaw infers them
|
||||
from normal conversations, scopes them to the same agent and channel, and
|
||||
delivers due check-ins through heartbeat. Exact user-requested reminders still
|
||||
belong to cron.
|
||||
|
||||
See [Inferred Commitments](/concepts/commitments).
|
||||
|
||||
### Task Flow
|
||||
|
||||
Task Flow is the flow orchestration substrate above background tasks. It manages durable multi-step flows with managed and mirrored sync modes, revision tracking, and `openclaw tasks flow list|show|cancel` for inspection.
|
||||
@@ -125,7 +111,6 @@ See [Heartbeat](/gateway/heartbeat).
|
||||
## Related
|
||||
|
||||
- [Scheduled Tasks](/automation/cron-jobs) — precise scheduling and one-shot reminders
|
||||
- [Inferred Commitments](/concepts/commitments) — memory-like follow-up check-ins
|
||||
- [Background Tasks](/automation/tasks) — task ledger for all detached work
|
||||
- [Task Flow](/automation/taskflow) — durable multi-step flow orchestration
|
||||
- [Hooks](/automation/hooks) — event-driven lifecycle scripts
|
||||
|
||||
@@ -173,7 +173,7 @@ Use an agent override when several agents share the same room but only one shoul
|
||||
}
|
||||
```
|
||||
|
||||
The agent-specific `agents.list[].groupChat.unmentionedInbound` value overrides `messages.groupChat.unmentionedInbound` for that agent.
|
||||
The agent-specific `agents.entries.*.groupChat.unmentionedInbound` value overrides `messages.groupChat.unmentionedInbound` for that agent.
|
||||
|
||||
## Visible reply modes
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ Add a top-level `broadcast` section (next to `bindings`). Keys are WhatsApp peer
|
||||
|
||||
**Result:** when OpenClaw would reply in this chat, it runs all three agents.
|
||||
|
||||
Every listed agent id must exist in `agents.list`: config validation reports unknown ids, and the runtime skips them with a `Broadcast agent <id> not found in agents.list; skipping` warning.
|
||||
Every listed agent id must exist in `agents.entries`: config validation reports unknown ids, and the runtime skips them with a `Broadcast agent <id> not found in agents.entries; skipping` warning.
|
||||
|
||||
### Processing strategy
|
||||
|
||||
@@ -247,7 +247,7 @@ Broadcast groups work alongside existing routing:
|
||||
<Accordion title="Agents not responding">
|
||||
**Check:**
|
||||
|
||||
1. Agent IDs exist in `agents.list` (config validation rejects unknown ids).
|
||||
1. Agent IDs exist in `agents.entries` (config validation rejects unknown ids).
|
||||
2. Peer ID format is correct (group JID like `120363403215116621@g.us`, or E.164 like `+15551234567` for DMs).
|
||||
3. The message passed normal gating (mention/activation rules still apply).
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ Routing picks **one agent** for each inbound message:
|
||||
6. **Team match** (Slack) via `teamId`.
|
||||
7. **Account match** (`accountId` on the channel).
|
||||
8. **Channel match** (any account on that channel, `accountId: "*"`).
|
||||
9. **Default agent** (`agents.list[].default`, else first list entry, fallback to `main`).
|
||||
9. **Default agent** (`agents.entries.*.default`, else first list entry, fallback to `main`).
|
||||
|
||||
When a binding includes multiple match fields (`peer`, `guildId`, `teamId`, `roles`), **all provided fields must match** for that binding to apply.
|
||||
|
||||
@@ -116,7 +116,7 @@ See: [Broadcast Groups](/channels/broadcast-groups).
|
||||
|
||||
## Config overview
|
||||
|
||||
- `agents.list`: named agent definitions (workspace, model, etc.).
|
||||
- `agents.entries`: named agent definitions (workspace, model, etc.).
|
||||
- `bindings`: map inbound channels/accounts/peers to agents.
|
||||
|
||||
Example:
|
||||
|
||||
+12
-22
@@ -574,7 +574,7 @@ Example:
|
||||
Mention detection includes:
|
||||
|
||||
- explicit bot mention
|
||||
- configured mention patterns (`agents.list[].groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`)
|
||||
- configured mention patterns (`agents.entries.*.groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`)
|
||||
- implicit reply-to-bot behavior in supported cases
|
||||
|
||||
When writing outbound Discord messages, use canonical mention syntax: `<@USER_ID>` for users, `<#CHANNEL_ID>` for channels, and `<@&ROLE_ID>` for roles. Do not use the legacy `<@!USER_ID>` nickname mention form.
|
||||
@@ -762,17 +762,8 @@ See [Slash commands](/tools/slash-commands) for the command catalog and behavior
|
||||
enabled: true,
|
||||
idleHours: 24,
|
||||
maxAgeHours: 0,
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
discord: {
|
||||
threadBindings: {
|
||||
enabled: true,
|
||||
idleHours: 24,
|
||||
maxAgeHours: 0,
|
||||
spawnSessions: true,
|
||||
defaultSpawnContext: "fork",
|
||||
},
|
||||
spawnSessions: true,
|
||||
defaultSpawnContext: "fork",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -780,11 +771,11 @@ See [Slash commands](/tools/slash-commands) for the command catalog and behavior
|
||||
|
||||
Notes:
|
||||
|
||||
- `session.threadBindings.*` sets global defaults; `channels.discord.threadBindings.*` overrides Discord behavior.
|
||||
- `session.threadBindings.*` is the canonical policy for Discord and Telegram.
|
||||
- `spawnSessions` controls auto-create/bind threads for `sessions_spawn({ thread: true })` and ACP thread spawns. Default: `true`.
|
||||
- `defaultSpawnContext` controls native subagent context for thread-bound spawns. Default: `"fork"`.
|
||||
- Deprecated `spawnSubagentSessions`/`spawnAcpSessions` keys are migrated by `openclaw doctor --fix`.
|
||||
- If thread bindings are disabled for an account, `/focus` and related thread binding operations are unavailable.
|
||||
- If thread bindings are disabled, `/focus` and related operations are unavailable.
|
||||
|
||||
See [Sub-agents](/tools/subagents), [ACP Agents](/tools/acp-agents), and [Configuration Reference](/gateway/configuration-reference).
|
||||
|
||||
@@ -817,9 +808,8 @@ See [Slash commands](/tools/slash-commands) for the command catalog and behavior
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
id: "codex",
|
||||
entries: {
|
||||
codex: {
|
||||
runtime: {
|
||||
type: "acp",
|
||||
acp: {
|
||||
@@ -830,7 +820,7 @@ See [Slash commands](/tools/slash-commands) for the command catalog and behavior
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
bindings: [
|
||||
{
|
||||
@@ -918,7 +908,7 @@ See [Slash commands](/tools/slash-commands) for the command catalog and behavior
|
||||
- `channels.discord.accounts.<accountId>.ackReaction`
|
||||
- `channels.discord.ackReaction`
|
||||
- `messages.ackReaction`
|
||||
- agent identity emoji fallback (`agents.list[].identity.emoji`, else "👀")
|
||||
- agent identity emoji fallback (`agents.entries.*.identity.emoji`, else "👀")
|
||||
|
||||
Notes:
|
||||
|
||||
@@ -1263,7 +1253,7 @@ Notes:
|
||||
- The OpenAI realtime provider accepts current Realtime 2 event names and legacy Codex-compatible aliases for output audio and transcript events, so compatible provider snapshots can drift without dropping assistant audio.
|
||||
- `voice.realtime.bargeIn` controls whether Discord speaker-start events interrupt active realtime playback. If unset, it follows the realtime provider's input-audio interruption setting.
|
||||
- `voice.realtime.minBargeInAudioEndMs` controls the minimum assistant playback duration before an OpenAI realtime barge-in truncates audio. Default: `250`. Set `0` for immediate interruption in low-echo rooms, or raise it for echo-heavy speaker setups.
|
||||
- `voice.tts` overrides `messages.tts` for `stt-tts` voice playback only; realtime modes use `voice.realtime.speakerVoice` instead. For an OpenAI voice on Discord playback, set `voice.tts.provider: "openai"` and choose a Text-to-speech voice under `voice.tts.providers.openai.speakerVoice`. `cedar` is a good masculine-sounding choice on the current OpenAI TTS model.
|
||||
- `voice.tts` overrides `tts` for `stt-tts` voice playback only; realtime modes use `voice.realtime.speakerVoice` instead. For an OpenAI voice on Discord playback, set `voice.tts.provider: "openai"` and choose a Text-to-speech voice under `voice.tts.providers.openai.speakerVoice`. `cedar` is a good masculine-sounding choice on the current OpenAI TTS model.
|
||||
- Per-channel Discord `systemPrompt` overrides apply to voice transcript turns for that voice channel.
|
||||
- When OpenClaw joins a voice channel, the routed agent session receives a silent system event with the current participant roster. Later participant joins and leaves update that session without triggering an unsolicited spoken reply; Discord display names are treated as untrusted labels. Authorized voice turns also receive a fresh roster snapshot.
|
||||
- Voice transcript turns and `/vc` commands use Discord entries in `commands.ownerAllowFrom` for owner status. When no Discord command owner is configured, the selected Discord account's `allowFrom` (or legacy `dm.allowFrom`) can still authorize voice access without granting owner status. Agent tool visibility follows the configured tool policy for the routed session.
|
||||
@@ -1337,7 +1327,7 @@ STT plus TTS pipeline:
|
||||
- `tools.media.audio` handles STT, for example `openai/gpt-4o-mini-transcribe`.
|
||||
- The transcript is sent through Discord ingress and routing while the response LLM runs with a voice-output policy that hides the agent `tts` tool and asks for returned text, because Discord voice owns final TTS playback.
|
||||
- `voice.model`, when set, overrides only the response LLM for this voice-channel turn.
|
||||
- `voice.tts` is merged over `messages.tts`; streaming-capable providers feed the player directly, otherwise the resulting audio file is played in the joined channel.
|
||||
- `voice.tts` is merged over `tts`; streaming-capable providers feed the player directly, otherwise the resulting audio file is played in the joined channel.
|
||||
|
||||
Default agent-proxy voice-channel session example:
|
||||
|
||||
@@ -1522,7 +1512,7 @@ Common patterns:
|
||||
- `capture ignored during playback (barge-in disabled)` means OpenClaw intentionally dropped input while assistant audio was active. Enable `voice.realtime.bargeIn` if you want speech to interrupt playback.
|
||||
- `barge-in ignored ... outputActive=false` means Discord or provider VAD reported speech, but OpenClaw had no active playback to interrupt. This should not cut off audio.
|
||||
|
||||
Credentials are resolved per component: LLM route auth for `voice.model`, STT auth for `tools.media.audio`, TTS auth for `messages.tts`/`voice.tts`, and realtime provider auth for `voice.realtime.providers` or the provider's normal auth config.
|
||||
Credentials are resolved per component: LLM route auth for `voice.model`, STT auth for `tools.media.audio`, TTS auth for `tts`/`voice.tts`, and realtime provider auth for `voice.realtime.providers` or the provider's normal auth config.
|
||||
|
||||
### Voice messages
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ The official `lark-cli` VC agent skill currently marks meeting-bot actions as a
|
||||
```
|
||||
|
||||
`defaultAccount` controls which account is used when outbound APIs do not specify an `accountId`. Account entries inherit top-level settings; most top-level keys can be overridden per account.
|
||||
`accounts.<id>.tts` uses the same shape as `messages.tts` and deep-merges over global TTS config, so multi-bot Feishu setups can keep shared provider credentials globally while overriding only voice, model, persona, or auto mode per account.
|
||||
`accounts.<id>.tts` uses the same shape as `tts` and deep-merges over global TTS config, so multi-bot Feishu setups can keep shared provider credentials globally while overriding only voice, model, persona, or auto mode per account.
|
||||
|
||||
### Message limits
|
||||
|
||||
@@ -642,7 +642,7 @@ Full configuration: [Gateway configuration](/gateway/configuration)
|
||||
| `channels.feishu.accounts.<id>.appId` | App ID | - |
|
||||
| `channels.feishu.accounts.<id>.appSecret` | App Secret | - |
|
||||
| `channels.feishu.accounts.<id>.domain` | Per-account domain override | `feishu` |
|
||||
| `channels.feishu.accounts.<id>.tts` | Per-account TTS override | `messages.tts` |
|
||||
| `channels.feishu.accounts.<id>.tts` | Per-account TTS override | `tts` |
|
||||
| `channels.feishu.dmPolicy` | DM policy (`pairing`, `allowlist`, `open`) | `pairing` |
|
||||
| `channels.feishu.allowFrom` | DM allowlist (open_id list) | - |
|
||||
| `channels.feishu.groupPolicy` | Group policy (`open`, `allowlist`, `disabled`) | `allowlist` |
|
||||
|
||||
@@ -172,10 +172,8 @@ Use these identifiers for delivery and allowlists:
|
||||
webhookPath: "/googlechat",
|
||||
botUser: "users/1234567890", // optional; helps mention detection
|
||||
allowBots: false,
|
||||
dm: {
|
||||
policy: "pairing",
|
||||
allowFrom: ["users/1234567890"],
|
||||
},
|
||||
dmPolicy: "pairing",
|
||||
allowFrom: ["users/1234567890"],
|
||||
groupPolicy: "allowlist",
|
||||
groups: {
|
||||
"spaces/AAAA": {
|
||||
|
||||
@@ -13,7 +13,7 @@ For the cross-channel groups model (Discord, iMessage, Matrix, Microsoft Teams,
|
||||
Goal: let OpenClaw sit in WhatsApp groups, wake up only when pinged, and keep that thread separate from the personal DM session.
|
||||
|
||||
<Note>
|
||||
`agents.list[].groupChat.mentionPatterns` is shared with the other channels' mention gating. For multi-agent setups, set it per agent, or use `messages.groupChat.mentionPatterns` as a global fallback. With neither set, patterns are derived from the agent identity name/emoji.
|
||||
`agents.entries.*.groupChat.mentionPatterns` is shared with the other channels' mention gating. For multi-agent setups, set it per agent, or use `messages.groupChat.mentionPatterns` as a global fallback. With neither set, patterns are derived from the agent identity name/emoji.
|
||||
</Note>
|
||||
|
||||
## Behavior
|
||||
@@ -42,14 +42,13 @@ Make display-name pings work even when WhatsApp strips the visual `@` from the t
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
entries: {
|
||||
main: {
|
||||
groupChat: {
|
||||
mentionPatterns: ["@?openclaw", "\\+?15555550123"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
@@ -88,7 +87,7 @@ Only owner numbers (from `channels.whatsapp.allowFrom`, or the bot's own E.164 w
|
||||
- Heartbeats run in the agent's main session; group sessions never get heartbeat runs.
|
||||
- Echo suppression remembers the combined prompt (history + current message) per session so the bot's own delivered messages do not retrigger it; an identical repeated batch can be skipped as an echo.
|
||||
- Session store entries appear as `agent:<agentId>:whatsapp:group:<jid>` in the per-agent SQLite session store; a missing entry just means the group has not triggered a run yet.
|
||||
- Typing indicators follow `session.typingMode` / `agents.defaults.typingMode`. When visible replies are opted into message-tool-only mode, typing starts immediately by default so group members can see the agent working even if no automatic final reply is posted. Explicit typing-mode config still wins.
|
||||
- Typing indicators follow `agents.entries.*.typingMode` / `agents.defaults.typingMode`. When visible replies are opted into message-tool-only mode, typing starts immediately by default so group members can see the agent working even if no automatic final reply is posted. Explicit typing-mode config still wins.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -342,15 +342,14 @@ Each fact defaults to enabled when the channel produces it. Set the correspondin
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
entries: {
|
||||
main: {
|
||||
groupChat: {
|
||||
mentionPatterns: ["@openclaw", "openclaw", "\\+15555550123"],
|
||||
historyLimit: 50,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
@@ -429,7 +428,7 @@ Account-level channel configs can set the same policy under `channels.<channel>.
|
||||
<AccordionGroup>
|
||||
<Accordion title="Mention gating notes">
|
||||
- `mentionPatterns` are case-insensitive safe regex patterns; invalid patterns and unsafe nested-repetition forms are ignored (with a warning).
|
||||
- Pattern precedence: `agents.list[].groupChat.mentionPatterns` (useful when multiple agents share a group) overrides `messages.groupChat.mentionPatterns`; when neither is set, patterns are derived from the agent identity name/emoji.
|
||||
- Pattern precedence: `agents.entries.*.groupChat.mentionPatterns` (useful when multiple agents share a group) overrides `messages.groupChat.mentionPatterns`; when neither is set, patterns are derived from the agent identity name/emoji.
|
||||
- Mention gating is only enforced when mention detection is possible (native mentions or `mentionPatterns` are configured).
|
||||
- Allowlisting a group or sender does not disable mention gating; set that group's `requireMention` to `false` when all messages should trigger.
|
||||
- Automatic group chat prompt context carries the resolved silent-reply instruction every turn; workspace files should not duplicate `NO_REPLY` mechanics.
|
||||
|
||||
@@ -323,7 +323,7 @@ If disabling SIP is not acceptable for your threat model:
|
||||
Mention gating for groups:
|
||||
|
||||
- iMessage has no native mention metadata
|
||||
- mention detection uses regex patterns (`agents.list[].groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`)
|
||||
- mention detection uses regex patterns (`agents.entries.*.groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`)
|
||||
- with no configured patterns, mention gating cannot be enforced
|
||||
- control commands from authorized senders bypass mention gating
|
||||
|
||||
@@ -840,7 +840,7 @@ openclaw channels status --probe --channel imessage
|
||||
- `channels.imessage.groupPolicy`
|
||||
- `channels.imessage.groupAllowFrom`
|
||||
- `channels.imessage.groups` allowlist behavior
|
||||
- mention pattern configuration (`agents.list[].groupChat.mentionPatterns`)
|
||||
- mention pattern configuration (`agents.entries.*.groupChat.mentionPatterns`)
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -864,7 +864,7 @@ Room allowlist keys (`groups`, legacy `rooms`) should be room IDs or aliases. Pl
|
||||
|
||||
- `groupPolicy`: `"open"`, `"allowlist"`, or `"disabled"`. Default: `"allowlist"`.
|
||||
- `groupAllowFrom`: allowlist of user IDs for room traffic.
|
||||
- `mentionPatterns`: scoped regex patterns for room mentions. Object with `{ mode: "allow"|"deny", allowIn: [roomId, ...], denyIn: [roomId, ...] }`. Controls whether configured `agents.list[].groupChat.mentionPatterns` apply per-room.
|
||||
- `mentionPatterns`: scoped regex patterns for room mentions. Object with `{ mode: "allow"|"deny", allowIn: [roomId, ...], denyIn: [roomId, ...] }`. Controls whether configured `agents.entries.*.groupChat.mentionPatterns` apply per-room.
|
||||
- `dm.enabled`: when `false`, ignore all DMs. Default: `true`.
|
||||
- `dm.policy`: `"pairing"` (default), `"allowlist"`, `"open"`, or `"disabled"`. Applies after the bot has joined and classified the room as a DM; it does not affect invite handling.
|
||||
- `dm.allowFrom`: allowlist of user IDs for DM traffic.
|
||||
|
||||
@@ -252,10 +252,10 @@ commands run one by one, independent of any merge batch.
|
||||
|
||||
STT and TTS support two-level configuration with priority fallback:
|
||||
|
||||
| Setting | Plugin-specific | Framework fallback |
|
||||
| ------- | -------------------------------------------------------- | ----------------------------- |
|
||||
| STT | `channels.qqbot.stt` | `tools.media.audio.models[0]` |
|
||||
| TTS | `channels.qqbot.tts`, `channels.qqbot.accounts.<id>.tts` | `messages.tts` |
|
||||
| Setting | Plugin-specific | Framework fallback |
|
||||
| ------- | -------------------------------------------------------- | ------------------------------------------------ |
|
||||
| STT | `channels.qqbot.stt` | first audio-capable `tools.media.models[]` entry |
|
||||
| TTS | `channels.qqbot.tts`, `channels.qqbot.accounts.<id>.tts` | `tts` |
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -285,12 +285,11 @@ STT and TTS support two-level configuration with priority fallback:
|
||||
```
|
||||
|
||||
Set `enabled: false` on either to disable. Account-level TTS overrides use the
|
||||
same shape as `messages.tts` and deep-merge over channel/global TTS config.
|
||||
same shape as `tts` and deep-merge over channel/global TTS config.
|
||||
|
||||
STT requests time out after 60 seconds by default. Plugin-specific STT uses the
|
||||
selected `models.providers.<id>.timeoutSeconds` override. Framework audio STT
|
||||
uses `tools.media.audio.models[0].timeoutSeconds`, then
|
||||
`tools.media.audio.timeoutSeconds`, then the selected provider override.
|
||||
uses the selected audio-capable `tools.media.models[]` entry's `timeoutSeconds`, then the selected provider override.
|
||||
|
||||
Inbound QQ voice attachments are exposed to agents as audio media metadata
|
||||
while keeping raw voice files out of generic `MediaPaths`. `[[audio_as_voice]]`
|
||||
|
||||
@@ -453,8 +453,7 @@ Provider options:
|
||||
- `channels.signal.accountUuid`: optional bot account UUID for native @mention detection and loop protection.
|
||||
- `channels.signal.cliPath`: path to `signal-cli`.
|
||||
- `channels.signal.configPath`: optional `signal-cli --config` directory.
|
||||
- `channels.signal.httpUrl`: full daemon URL (overrides host/port).
|
||||
- `channels.signal.httpHost`, `channels.signal.httpPort`: daemon bind (default `127.0.0.1:8080`).
|
||||
- `channels.signal.httpUrl`: full daemon URL and canonical daemon bind (default `http://127.0.0.1:8080`).
|
||||
- `channels.signal.autoStart`: auto-spawn daemon (default true if `httpUrl` unset).
|
||||
- `channels.signal.startupTimeoutMs`: startup wait timeout in ms (min 1000, cap 120000; default 30000).
|
||||
- `channels.signal.receiveMode`: `on-start | manual`.
|
||||
@@ -484,9 +483,9 @@ Provider options:
|
||||
|
||||
Related global options:
|
||||
|
||||
- `agents.list[].groupChat.mentionPatterns` (plain-text fallback; Signal native @mentions are detected from structured metadata when the bot account identity is configured).
|
||||
- `agents.entries.*.groupChat.mentionPatterns` (plain-text fallback; Signal native @mentions are detected from structured metadata when the bot account identity is configured).
|
||||
- `messages.groupChat.mentionPatterns` (global fallback).
|
||||
- `messages.responsePrefix`.
|
||||
- `channels.signal.responsePrefix` or an account-level `responsePrefix`.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -1298,7 +1298,7 @@ Current Slack message actions include `send`, `upload-file`, `download-file`, `r
|
||||
|
||||
- explicit app mention (`<@botId>`)
|
||||
- Slack user-group mention (`<!subteam^S...>`) when the bot user is a member of that user group; requires `usergroups:read`
|
||||
- mention regex patterns (`agents.list[].groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`)
|
||||
- mention regex patterns (`agents.entries.*.groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`)
|
||||
- replies to the bot's own Slack message (`implicitMentions.replyToBot`)
|
||||
- follow-ups in threads where the bot participated (`implicitMentions.threadParticipation`)
|
||||
|
||||
@@ -1372,7 +1372,7 @@ Resolution order:
|
||||
- `channels.slack.accounts.<accountId>.ackReaction`
|
||||
- `channels.slack.ackReaction`
|
||||
- `messages.ackReaction`
|
||||
- agent identity emoji fallback (`agents.list[].identity.emoji`, else `"eyes"` / 👀)
|
||||
- agent identity emoji fallback (`agents.entries.*.identity.emoji`, else `"eyes"` / 👀)
|
||||
|
||||
Notes:
|
||||
|
||||
@@ -1507,7 +1507,7 @@ To speak to OpenClaw in Slack today, send a Slack audio clip to the OpenClaw app
|
||||
|
||||
Audio clips and Slackbot dictation have different privacy semantics: clips follow Slack file-retention policy and OpenClaw downloads them for transcription, while Slack says dictation audio is not stored.
|
||||
|
||||
In a channel with `requireMention: true`, a captionless audio clip can satisfy the gate by speaking a configured mention pattern (`agents.list[].groupChat.mentionPatterns`, falling back to `messages.groupChat.mentionPatterns`). OpenClaw authorizes the sender before downloading or transcribing the clip, then admits it only when the transcript matches. A failed or nonmatching speculative transcript is discarded with the downloaded clip; it is not retained in channel history. Native Slack `@bot` identity cannot be inferred from speech, so configure a spoken-name pattern or include a typed mention. If transcript echoing is enabled, the echo is sent only after admission.
|
||||
In a channel with `requireMention: true`, a captionless audio clip can satisfy the gate by speaking a configured mention pattern (`agents.entries.*.groupChat.mentionPatterns`, falling back to `messages.groupChat.mentionPatterns`). OpenClaw authorizes the sender before downloading or transcribing the clip, then admits it only when the transcript matches. A failed or nonmatching speculative transcript is discarded with the downloaded clip; it is not retained in channel history. Native Slack `@bot` identity cannot be inferred from speech, so configure a spoken-name pattern or include a typed mention. If transcript echoing is enabled, the echo is sent only after admission.
|
||||
|
||||
## Media, chunking, and delivery
|
||||
|
||||
@@ -2057,7 +2057,7 @@ When a single Slack message contains multiple file attachments:
|
||||
### Size, download, and model limits
|
||||
|
||||
- **Size cap**: Default 20 MB per file. Configurable via `channels.slack.mediaMaxMb`.
|
||||
- **Audio transcription cap**: `tools.media.audio.maxBytes` also applies when the downloaded file is sent to a transcription provider or CLI.
|
||||
- **Audio transcription cap**: the selected audio-capable `tools.media.models[]` entry's `maxBytes` also applies when the downloaded file is sent to a transcription provider or CLI.
|
||||
- **Download failures**: Files that Slack cannot serve, expired URLs, inaccessible files, oversize files, and Slack auth/login HTML responses are skipped instead of being reported as unsupported formats.
|
||||
- **Vision model**: Image analysis uses the active reply model when it supports vision, or the image model configured at `agents.defaults.imageModel`.
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
Group replies require mention by default. A mention can come from:
|
||||
|
||||
- a native `@botusername` mention, or
|
||||
- a mention pattern in `agents.list[].groupChat.mentionPatterns` or `messages.groupChat.mentionPatterns`
|
||||
- a mention pattern in `agents.entries.*.groupChat.mentionPatterns` or `messages.groupChat.mentionPatterns`
|
||||
|
||||
Session-level toggles (state only, not persisted): `/activation always`, `/activation mention`. Use config for persistence:
|
||||
|
||||
@@ -578,7 +578,7 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
|
||||
**Persistent ACP topic binding**: forum topics can pin ACP harness sessions through top-level typed bindings (`bindings[]` with `type: "acp"`, `match.channel: "telegram"`, `peer.kind: "group"`, and a topic-qualified id like `-1001234567890:topic:42`). Currently scoped to forum topics in groups/supergroups. See [ACP Agents](/tools/acp-agents).
|
||||
|
||||
**Thread-bound ACP spawn from chat**: `/acp spawn <agent> --thread here|auto` binds the current topic to a new ACP session; follow-ups route there directly, and OpenClaw pins the spawn confirmation in-topic. Requires `channels.telegram.threadBindings.spawnSessions` (default: `true`).
|
||||
**Thread-bound ACP spawn from chat**: `/acp spawn <agent> --thread here|auto` binds the current topic to a new ACP session; follow-ups route there directly, and OpenClaw pins the spawn confirmation in-topic. Controlled by `session.threadBindings.spawnSessions` (default: `true`).
|
||||
|
||||
Template context exposes `MessageThreadId` and `IsForum`. DM chats with `message_thread_id` keep reply metadata but only use thread-aware session keys when Telegram `getMe` reports `has_topics_enabled: true`.
|
||||
The retired `dm.threadReplies` and `direct.*.threadReplies` overrides are gone; BotFather threaded mode is the single source of truth. Run `openclaw doctor --fix` to remove stale config keys.
|
||||
@@ -699,7 +699,7 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
- `channels.telegram.accounts.<accountId>.ackReaction`
|
||||
- `channels.telegram.ackReaction`
|
||||
- `messages.ackReaction`
|
||||
- agent identity emoji fallback (`agents.list[].identity.emoji`, else "👀")
|
||||
- agent identity emoji fallback (`agents.entries.*.identity.emoji`, else "👀")
|
||||
|
||||
Telegram expects a unicode emoji (for example "👀"); use `""` to disable the reaction for a channel or account.
|
||||
|
||||
|
||||
@@ -295,7 +295,7 @@ Scope the opt-in to one account under `channels.whatsapp.accounts.<id>.pluginHoo
|
||||
Group replies require a mention by default. Mention detection includes:
|
||||
|
||||
- explicit WhatsApp mentions of the bot identity
|
||||
- configured mention regex patterns (`agents.list[].groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`)
|
||||
- configured mention regex patterns (`agents.entries.*.groupChat.mentionPatterns`, fallback `messages.groupChat.mentionPatterns`)
|
||||
- inbound voice-note transcripts for authorized group messages
|
||||
- implicit reply-to-bot detection (reply sender matches bot identity)
|
||||
|
||||
@@ -339,7 +339,7 @@ Direct chats match E.164 numbers; groups match WhatsApp group JIDs. Group allowl
|
||||
|
||||
## Personal-number and self-chat behavior
|
||||
|
||||
When the linked self number is also present in `allowFrom`, self-chat safeguards activate: skip read receipts for self-chat turns, ignore mention-JID auto-trigger behavior that would ping yourself, and default replies to `[{identity.name}]` (or `[openclaw]`) when `messages.responsePrefix` is unset.
|
||||
When the linked self number is also present in `allowFrom`, self-chat safeguards activate: skip read receipts for self-chat turns, ignore mention-JID auto-trigger behavior that would ping yourself, and default replies to `[{identity.name}]` (or `[openclaw]`) when the channel/account `responsePrefix` is unset.
|
||||
|
||||
## Message normalization and context
|
||||
|
||||
@@ -668,7 +668,7 @@ Primary reference: [Configuration reference - WhatsApp](/gateway/config-channels
|
||||
| Access | `dmPolicy`, `allowFrom`, `groupPolicy`, `groupAllowFrom`, `groups` |
|
||||
| Delivery | `textChunkLimit`, `streaming.chunkMode`, `mediaMaxMb`, `sendReadReceipts`, `ackReaction`, `reactionLevel` |
|
||||
| Multi-account | `accounts.<id>.enabled`, `accounts.<id>.authDir`, and other per-account overrides |
|
||||
| Operations | `configWrites`, `debounceMs`, `web.enabled` |
|
||||
| Operations | `configWrites`, `debounceMs`, `enabled` |
|
||||
| Session behavior | `session.dmScope`, `historyLimit`, `dmHistoryLimit`, `dms.<id>.historyLimit` |
|
||||
| Prompts | `groups.<id>.systemPrompt`, `groups["*"].systemPrompt`, `direct.<id>.systemPrompt`, `direct["*"].systemPrompt` |
|
||||
|
||||
|
||||
+20
@@ -207,6 +207,22 @@ The changed-target PR plan reduces the common Node test burst from 14 Blacksmith
|
||||
|
||||
Canonical-repo CI keeps Blacksmith as the default runner path for normal push and pull-request runs. `workflow_dispatch` and non-canonical repository runs use GitHub-hosted runners, but normal canonical runs do not currently probe Blacksmith queue health or automatically fall back to GitHub-hosted labels when Blacksmith is unavailable.
|
||||
|
||||
## Surface ratchets
|
||||
|
||||
Two shrink-only budgets guard the configuration surface. Both fail CI on growth
|
||||
until the budget file is consciously updated in the same PR, and both demand a
|
||||
ratchet-down when cleanup lowers the real count.
|
||||
|
||||
- `config/env-var-count-budget.txt` caps the number of distinct `OPENCLAW_*`
|
||||
names in production source under `src/`, `packages/`, and `extensions/`
|
||||
(tests and QA Lab excluded). Checked by `node scripts/check-env-var-count.mjs`.
|
||||
Removing env vars: lower the number in the same PR. Adding one is a
|
||||
config-surface decision — justify it in the PR body.
|
||||
- `docs/.generated/config-baseline.counts.json` caps the per-kind
|
||||
(core/channel/plugin) `openclaw.json` schema entry counts. Checked by
|
||||
`pnpm config:docs:check`; regenerate with `pnpm config:docs:gen` after any
|
||||
schema change.
|
||||
|
||||
## Local equivalents
|
||||
|
||||
```bash
|
||||
@@ -632,6 +648,10 @@ gh workflow run duplicate-after-merge.yml \
|
||||
|
||||
## Local check gates and changed routing
|
||||
|
||||
### Config baseline count ratchet
|
||||
|
||||
`pnpm config:docs:check` rejects undocumented config-surface growth and corrupt or stale count snapshots. When a reviewed product change intentionally adds schema paths, run `pnpm config:docs:gen`, inspect the core/channel/plugin count deltas and generated SHA-256 files, and commit the conscious baseline bump with the schema, help, labels, migration, and tests. Do not hand-edit the counts file to bypass the ratchet.
|
||||
|
||||
Local changed-lane logic lives in `scripts/changed-lanes.mjs` and is executed by `scripts/check-changed.mjs`. That local check gate is stricter about architecture boundaries than the broad CI platform scope:
|
||||
|
||||
- core production changes run core prod and core test typecheck plus core lint/guards;
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ Options: `--force`, `--json`.
|
||||
|
||||
Use routing bindings to pin inbound channel traffic to a specific agent.
|
||||
|
||||
If you also want different visible skills per agent, configure `agents.defaults.skills` and `agents.list[].skills` in `openclaw.json`. See [Skills config](/tools/skills-config) and [Configuration reference](/gateway/config-agents#agentsdefaultsskills).
|
||||
If you also want different visible skills per agent, configure `agents.defaults.skills` and `agents.entries.*.skills` in `openclaw.json`. See [Skills config](/tools/skills-config) and [Configuration reference](/gateway/config-agents#agentsdefaultsskills).
|
||||
|
||||
List bindings:
|
||||
|
||||
@@ -152,7 +152,7 @@ Avatar paths resolve relative to the workspace root and cannot escape it, even t
|
||||
|
||||
## Set identity
|
||||
|
||||
`set-identity` writes fields into `agents.list[].identity`: `name`, `theme`, `emoji`, `avatar` (workspace-relative path, http(s) URL, or data URI).
|
||||
`set-identity` writes fields into `agents.entries.*.identity`: `name`, `theme`, `emoji`, `avatar` (workspace-relative path, http(s) URL, or data URI).
|
||||
|
||||
- `--agent` or `--workspace` selects the target agent. If `--workspace` matches more than one agent, the command fails and asks you to pass `--agent`.
|
||||
- Local workspace-relative avatar image files are limited to 2 MB. HTTP(S) URLs and `data:` URIs are not checked against the local file-size limit.
|
||||
|
||||
@@ -140,8 +140,7 @@ This changes the **host approvals file** only. To keep the requested OpenClaw po
|
||||
|
||||
```bash
|
||||
openclaw config set tools.exec.host gateway
|
||||
openclaw config set tools.exec.security full
|
||||
openclaw config set tools.exec.ask off
|
||||
openclaw config set tools.exec.mode full
|
||||
```
|
||||
|
||||
`tools.exec.host=gateway` is explicit here because `host=auto` still means "sandbox when available, otherwise gateway": YOLO is about approvals, not routing. Use `gateway` (or `/exec host=gateway`) when you want host exec even with a sandbox configured.
|
||||
|
||||
@@ -7,11 +7,9 @@ read_when:
|
||||
title: "`openclaw commitments`"
|
||||
---
|
||||
|
||||
List and manage inferred follow-up commitments.
|
||||
|
||||
Commitments are opt-in (`commitments.enabled`), short-lived follow-up memories
|
||||
created from conversation context and delivered by heartbeat. See
|
||||
[Inferred commitments](/concepts/commitments) for the conceptual guide and config.
|
||||
Inspect and dismiss records left by the retired inferred commitments experiment.
|
||||
OpenClaw no longer creates or delivers new commitments, but keeps the maintenance
|
||||
command so upgrades can audit and clean up existing SQLite rows.
|
||||
|
||||
With no subcommand, `openclaw commitments` lists pending commitments.
|
||||
|
||||
@@ -31,8 +29,7 @@ openclaw commitments dismiss <id...> [--json]
|
||||
`dismissed`, `snoozed`, or `expired`. Unknown values exit with an error.
|
||||
- `--json`: output machine-readable JSON.
|
||||
|
||||
`dismiss` marks the given commitment ids as `dismissed` so heartbeat will not
|
||||
deliver them.
|
||||
`dismiss` marks the given commitment ids as `dismissed`.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
+6
-6
@@ -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.list[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.list[0].id'
|
||||
openclaw config get agents.list
|
||||
openclaw config set 'agents.list[1].tools.exec.node' "node-id-or-name"
|
||||
openclaw config get agents.entries.main
|
||||
openclaw config get agents.entries
|
||||
openclaw config set 'agents.entries.work.tools.exec.node' "node-id-or-name"
|
||||
```
|
||||
|
||||
### `config get`
|
||||
@@ -117,10 +117,10 @@ openclaw config set channels.whatsapp.groups '["*"]' --strict-json
|
||||
|
||||
`config get <path> --json` prints the raw value as JSON instead of terminal-formatted text.
|
||||
|
||||
When a write changes `agents.defaults.model` or a per-agent `agents.list[].model`, OpenClaw resolves each changed primary or fallback through the configured provider catalogs before writing. Unknown model references are rejected without changing the active config; run `openclaw models list` to see available models.
|
||||
When a write changes `agents.defaults.model` or a per-agent `agents.entries.*.model`, OpenClaw resolves each changed primary or fallback through the configured provider catalogs before writing. Unknown model references are rejected without changing the active config; run `openclaw models list` to see available models.
|
||||
|
||||
<Note>
|
||||
Object assignment replaces the target path by default. Protected paths that commonly hold user-added entries refuse replacements that would remove existing entries unless you pass `--replace`: `agents.defaults.models`, `agents.list`, `models.providers`, `models.providers.<id>`, `models.providers.<id>.models`, `plugins.entries`, and `auth.profiles`.
|
||||
Object assignment replaces the target path by default. Protected paths that commonly hold user-added entries refuse replacements that would remove existing entries unless you pass `--replace`: `agents.defaults.models`, `agents.entries`, `models.providers`, `models.providers.<id>`, `models.providers.<id>.models`, `plugins.entries`, and `auth.profiles`.
|
||||
</Note>
|
||||
|
||||
Use `--merge` when adding entries to those maps:
|
||||
|
||||
+2
-2
@@ -90,8 +90,8 @@ Hook packs install through the unified plugins installer/updater; `openclaw hook
|
||||
- Bare specs and `@latest` stay on the stable track; if npm resolves to a prerelease, OpenClaw stops and asks you to opt in explicitly (`@beta`, `@rc`, or an exact prerelease version).
|
||||
- Supported archives: `.zip`, `.tgz`, `.tar.gz`, `.tar`.
|
||||
- `-l, --link` links a local directory instead of copying it (adds it to `hooks.internal.load.extraDirs`); linked hook packs are managed hooks from an operator-configured directory, not workspace hooks.
|
||||
- `--pin` records npm installs as an exact resolved `name@version` in `hooks.internal.installs`.
|
||||
- Install copies the pack into `~/.openclaw/hooks/<id>`, enables its hooks under `hooks.internal.entries.*`, and records the install under `hooks.internal.installs`.
|
||||
- `--pin` records npm installs as an exact resolved `name@version` in shared SQLite state.
|
||||
- Install copies the pack into `~/.openclaw/hooks/<id>`, enables its hooks under `hooks.internal.entries.*`, and records install provenance in shared SQLite state.
|
||||
- If a stored integrity hash no longer matches the fetched artifact, OpenClaw warns and prompts before continuing; pass global `--yes` to bypass the prompt (for example in CI).
|
||||
|
||||
## Bundled hooks
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ Related: [Memory](/concepts/memory) concept, [Dreaming](/concepts/dreaming),
|
||||
openclaw memory status [--agent <id>] [--deep] [--index] [--fix] [--json] [--verbose]
|
||||
```
|
||||
|
||||
Without `--agent`, runs for every agent in `agents.list`; if no agent list is
|
||||
Without `--agent`, runs for every agent in `agents.entries`; if no agent list is
|
||||
configured, falls back to the default agent.
|
||||
|
||||
| Flag | Effect |
|
||||
@@ -40,7 +40,7 @@ scheduled sweeps never seem to run, the managed dreaming cron depends on the
|
||||
default agent's heartbeat firing to trigger reconciliation. See
|
||||
[Dreaming](/concepts/dreaming) for scheduling details.
|
||||
|
||||
Status also lists any extra search paths from `agents.defaults.memorySearch.extraPaths`.
|
||||
Status also lists any extra search paths from `memory.search.extraPaths`.
|
||||
|
||||
## `memory index`
|
||||
|
||||
|
||||
+5
-21
@@ -303,7 +303,7 @@ Security contract for remote rescue:
|
||||
- Disabled when sandboxing is active for the agent/session; OpenClaw refuses remote rescue and points to local CLI repair.
|
||||
- Default effective state is `auto`: allow remote rescue only in trusted YOLO operation, where the runtime already has unsandboxed local authority (`tools.exec.security` resolves to `full` and `tools.exec.ask` resolves to `off`, with sandbox mode `off`).
|
||||
- Requires an explicit owner identity; no wildcard sender rules, open group policy, unauthenticated webhooks, or anonymous channels.
|
||||
- Owner DMs only by default; group/channel rescue needs explicit opt-in.
|
||||
- Rescue is limited to owner DMs.
|
||||
- Plugin search and list are read-only. Plugin install is always local-only (blocked in rescue, even when otherwise enabled) because it downloads executable code. Plugin uninstall is refused in both local OpenClaw and rescue; run `openclaw plugins uninstall <id>` from a terminal.
|
||||
- Remote rescue cannot open the local TUI or switch into an interactive agent session; use local `openclaw` for agent handoff.
|
||||
- Persistent writes still require approval, even in rescue mode.
|
||||
@@ -312,26 +312,10 @@ Security contract for remote rescue:
|
||||
- Secrets are never echoed. SecretRef inspection reports availability, not values.
|
||||
- If the Gateway is alive, rescue prefers Gateway typed operations; if it is dead, rescue uses only the minimal local repair surface that does not depend on the normal agent loop.
|
||||
|
||||
Config shape:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"systemAgent": {
|
||||
"rescue": {
|
||||
"enabled": "auto",
|
||||
"ownerDmOnly": true,
|
||||
"pendingTtlMinutes": 15,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `enabled`: `"auto"` (default) allows rescue only when the effective runtime is YOLO and sandboxing is off; `false` never allows message-channel rescue; `true` explicitly allows rescue when owner/channel checks pass (still subject to the sandboxing denial).
|
||||
- `ownerDmOnly`: restrict rescue to owner direct messages. Default `true`.
|
||||
- `pendingTtlMinutes`: how long a pending rescue write stays open for `/openclaw yes` approval before expiring. Default `15`.
|
||||
|
||||
`openclaw doctor --fix` migrates the legacy `crestodian` config block to
|
||||
`systemAgent`. Runtime reads only the canonical block.
|
||||
Rescue policy is built in: it is available only when the effective runtime is
|
||||
YOLO, sandboxing is off, and the request is an owner DM. Pending write approvals
|
||||
expire after 15 minutes. `openclaw doctor --fix` removes the retired
|
||||
`systemAgent` and `crestodian` config blocks.
|
||||
|
||||
Remote rescue is covered by the Docker lane:
|
||||
|
||||
|
||||
+1
-1
@@ -436,7 +436,7 @@ openclaw plugins update openclaw-codex-app-server --acknowledge-clawhub-risk
|
||||
openclaw plugins update openclaw-codex-app-server --dangerously-force-unsafe-install
|
||||
```
|
||||
|
||||
Updates apply to tracked plugin installs in the managed plugin index and tracked hook-pack installs in `hooks.internal.installs`. They reuse the source that the user already chose when installing the plugin, so they do not require a second source acknowledgement.
|
||||
Updates apply to tracked plugin installs in the managed plugin index and tracked hook-pack installs in shared SQLite state. They reuse the source that the user already chose when installing the plugin, so they do not require a second source acknowledgement.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Resolving plugin id vs npm spec">
|
||||
|
||||
+22
-22
@@ -224,7 +224,7 @@ and the scoped rule can add its own finding against the same evidence.
|
||||
| `agentIds` | `tools`, `agents.workspace`, `sandbox`, `dataHandling.memory`, `execApprovals` | One or more runtime agents need stricter rules. |
|
||||
| `channelIds` | `ingress.channels` | One or more channels need stricter ingress rules. |
|
||||
|
||||
If an `agentIds` entry is not present in `agents.list[]`, OpenClaw evaluates
|
||||
If an `agentIds` entry is not present in `agents.entries.*`, OpenClaw evaluates
|
||||
the scoped rule against inherited global/default posture for that runtime
|
||||
agent id instead of skipping it.
|
||||
|
||||
@@ -375,20 +375,20 @@ private messages.
|
||||
| `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.denyCommands` | `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.denyCommands` is an exact, case-sensitive policy 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
|
||||
|
||||
| Policy field | Observed state | Use when |
|
||||
| -------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| `agents.workspace.allowedAccess` | `agents.defaults.sandbox.workspaceAccess` and `agents.list[].sandbox.workspaceAccess` | Allow only sandbox workspace access values such as `none` or `ro`. |
|
||||
| `agents.workspace.denyTools` | Global and per-agent tool deny config | Require mutation tools (`exec`, `process`, `write`, `edit`, `apply_patch`) to be denied. |
|
||||
| Policy field | Observed state | Use when |
|
||||
| -------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| `agents.workspace.allowedAccess` | `agents.defaults.sandbox.workspaceAccess` and `agents.entries.*.sandbox.workspaceAccess` | Allow only sandbox workspace access values such as `none` or `ro`. |
|
||||
| `agents.workspace.denyTools` | Global and per-agent tool deny config | Require mutation tools (`exec`, `process`, `write`, `edit`, `apply_patch`) to be denied. |
|
||||
|
||||
#### Sandbox posture
|
||||
|
||||
@@ -409,12 +409,12 @@ allowlist such as `["all"]`.
|
||||
|
||||
#### Data Handling
|
||||
|
||||
| Policy field | Observed state | Use when |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
|
||||
| `dataHandling.sensitiveLogging.requireRedaction` | `logging.redactSensitive` | Set to `true` to reject `logging.redactSensitive: "off"`. |
|
||||
| `dataHandling.telemetry.denyContentCapture` | `diagnostics.otel.captureContent` | Set to `true` to reject telemetry content capture. |
|
||||
| `dataHandling.retention.requireSessionMaintenance` | `session.maintenance.mode` | Set to `true` to require effective session maintenance mode `enforce`. |
|
||||
| `dataHandling.memory.denySessionTranscriptIndexing` | `memory.qmd.sessions.enabled` and `agents.*.memorySearch.experimental.sessionMemory` | Set to `true` to reject session transcript indexing into memory. |
|
||||
| Policy field | Observed state | Use when |
|
||||
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
||||
| `dataHandling.sensitiveLogging.requireRedaction` | `logging.redactSensitive` | Set to `true` to reject `logging.redactSensitive: "off"`. |
|
||||
| `dataHandling.telemetry.denyContentCapture` | `diagnostics.otel.captureContent` | Set to `true` to reject telemetry content capture. |
|
||||
| `dataHandling.retention.requireSessionMaintenance` | `session.maintenance.mode` | Set to `true` to require effective session maintenance mode `enforce`. |
|
||||
| `dataHandling.memory.denySessionTranscriptIndexing` | `memory.qmd.sessions.enabled`, `memory.search.experimental.sessionMemory`, and per-agent overrides | Set to `true` to reject session transcript indexing into memory. |
|
||||
|
||||
#### Secrets
|
||||
|
||||
@@ -502,14 +502,14 @@ only reviewed exec approval posture for selected agents.
|
||||
|
||||
| Policy field | Observed state | Use when |
|
||||
| ------------------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| `tools.profiles.allow` | `tools.profile` and `agents.list[].tools.profile` | Allow only tool profile ids such as `minimal`, `messaging`, or `coding`. |
|
||||
| `tools.profiles.allow` | `tools.profile` and `agents.entries.*.tools.profile` | Allow only tool profile ids such as `minimal`, `messaging`, or `coding`. |
|
||||
| `tools.fs.requireWorkspaceOnly` | `tools.fs.workspaceOnly` and per-agent `tools.fs` overrides | Set to `true` to require workspace-only filesystem tool posture. |
|
||||
| `tools.exec.allowSecurity` | `tools.exec.security` and per-agent exec security | Allow only exec security modes such as `deny` or `allowlist`. |
|
||||
| `tools.exec.requireAsk` | `tools.exec.ask` and per-agent exec ask mode | Require approval posture such as `always`. |
|
||||
| `tools.exec.allowHosts` | `tools.exec.host` and per-agent exec host routing | Allow only exec host routing modes such as `sandbox`. |
|
||||
| `tools.elevated.allow` | `tools.elevated.enabled` and per-agent elevated posture | Set to `false` to require elevated tool mode to stay disabled. |
|
||||
| `tools.alsoAllow.expected` | `tools.alsoAllow` and per-agent `tools.alsoAllow` | Require exact `alsoAllow` entries and report missing or unexpected additive tool grants. |
|
||||
| `tools.denyTools` | `tools.deny` and `agents.list[].tools.deny` | Require configured tool deny lists to include tool ids or groups such as `group:runtime` and `group:fs`. |
|
||||
| `tools.denyTools` | `tools.deny` and `agents.entries.*.tools.deny` | Require configured tool deny lists to include tool ids or groups such as `group:runtime` and `group:fs`. |
|
||||
|
||||
## Run checks
|
||||
|
||||
@@ -961,10 +961,10 @@ Example findings:
|
||||
"message": "Gateway node command 'system.run' is denied by policy but not denied by OpenClaw config.",
|
||||
"source": "policy",
|
||||
"path": "openclaw config",
|
||||
"ocPath": "oc://openclaw.config/gateway/nodes/denyCommands",
|
||||
"target": "oc://openclaw.config/gateway/nodes/denyCommands",
|
||||
"ocPath": "oc://openclaw.config/gateway/nodes/commands/deny",
|
||||
"target": "oc://openclaw.config/gateway/nodes/commands/deny",
|
||||
"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."
|
||||
}
|
||||
```
|
||||
|
||||
@@ -996,7 +996,7 @@ workspace config:
|
||||
|
||||
- set `tools.elevated.enabled=false` when a global policy forbids elevated tools
|
||||
- add missing required-deny tool ids to `tools.deny` or
|
||||
`agents.list[].tools.deny` when policy requires those tools to be denied
|
||||
`agents.entries.*.tools.deny` when policy requires those tools to be denied
|
||||
- set insecure `gateway.controlUi.*` toggles to `false`
|
||||
- set `gateway.mode=local` when policy denies remote gateway mode
|
||||
- set reported `gateway.http.endpoints.*.enabled` paths to `false` when policy
|
||||
@@ -1019,7 +1019,7 @@ target.
|
||||
Scoped required-deny repairs are skipped when the finding reports inherited
|
||||
root `tools.deny`, because adding the required tool to root config would affect
|
||||
more than the scoped policy target. Agent-local required-deny repairs can update
|
||||
the reported `agents.list[].tools.deny` path.
|
||||
the reported `agents.entries.*.tools.deny` path.
|
||||
|
||||
Scoped channel ingress repairs are skipped when the finding reports inherited
|
||||
`channels.defaults.*`, because changing the shared channel default would affect
|
||||
@@ -1030,7 +1030,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.
|
||||
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ Run `openclaw doctor --fix` to migrate valid legacy entries into SQLite. Invalid
|
||||
|
||||
## Configuration
|
||||
|
||||
Sandbox settings live in `~/.openclaw/openclaw.json` under `agents.defaults.sandbox` (per-agent overrides go in `agents.list[].sandbox`):
|
||||
Sandbox settings live in `~/.openclaw/openclaw.json` under `agents.defaults.sandbox` (per-agent overrides go in `agents.entries.*.sandbox`):
|
||||
|
||||
```jsonc
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ and `openclaw memory status --deep`.
|
||||
|
||||
`status --json --all` reports memory details from the active memory plugin
|
||||
runtime selected by `plugins.slots.memory`. Custom memory plugins can leave
|
||||
built-in `agents.defaults.memorySearch.enabled` disabled and still report
|
||||
built-in `memory.search.enabled` disabled and still report
|
||||
their own files, chunks, vector, and FTS state.
|
||||
|
||||
## Related
|
||||
|
||||
@@ -23,14 +23,15 @@ private conversations with one per-agent setting:
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
id: "personal",
|
||||
memorySearch: {
|
||||
rememberAcrossConversations: true,
|
||||
entries: {
|
||||
personal: {
|
||||
memory: {
|
||||
search: {
|
||||
rememberAcrossConversations: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
@@ -158,7 +159,7 @@ personalization would be surprising.
|
||||
Active Memory has two activation paths:
|
||||
|
||||
1. **Remember across conversations** automatically targets agents whose
|
||||
effective `memorySearch.rememberAcrossConversations` setting is enabled, but
|
||||
effective `memory.search.rememberAcrossConversations` setting is enabled, but
|
||||
only for private direct or persistent explicit UI conversations.
|
||||
2. **Advanced Active Memory** targets agent IDs listed in
|
||||
`plugins.entries.active-memory.config.agents` and applies the plugin's chat
|
||||
@@ -227,7 +228,7 @@ config:
|
||||
|
||||
This only affects the current session; it does not change
|
||||
`plugins.entries.active-memory.config.enabled`, an agent's
|
||||
`memorySearch.rememberAcrossConversations` setting, or other global
|
||||
`memory.search.rememberAcrossConversations` setting, or other global
|
||||
configuration.
|
||||
|
||||
To pause/resume for all sessions instead, use the global form (requires
|
||||
@@ -494,7 +495,7 @@ Memory automatically uses `memory_recall`; no explicit `toolsAllow` is needed:
|
||||
```
|
||||
|
||||
This is the advanced Active Memory path for LanceDB's own stored memories.
|
||||
`memorySearch.rememberAcrossConversations` does not expose private session
|
||||
`memory.search.rememberAcrossConversations` does not expose private session
|
||||
transcripts through `memory_recall`. Use LanceDB's auto-recall or the advanced
|
||||
configuration above when LanceDB is the active memory provider.
|
||||
|
||||
@@ -730,7 +731,7 @@ If active memory is not showing up where you expect:
|
||||
|
||||
1. Confirm the plugin is enabled under `plugins.entries.active-memory.enabled`.
|
||||
2. For Remember across conversations, confirm the agent's effective
|
||||
`memorySearch.rememberAcrossConversations` setting is enabled, run
|
||||
`memory.search.rememberAcrossConversations` setting is enabled, run
|
||||
`openclaw doctor` to verify the current memory provider supports protected
|
||||
transcript recall, and confirm `config.toolsAllow` includes `memory_search`
|
||||
when explicitly configured. For advanced Active Memory, confirm the agent ID
|
||||
@@ -757,14 +758,14 @@ path.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Embedding provider switched or stopped working">
|
||||
If `memorySearch.provider` is unset, OpenClaw uses OpenAI embeddings. Set
|
||||
`memorySearch.provider` explicitly for Bedrock, DeepInfra, Gemini, GitHub
|
||||
If `memory.search.provider` is unset, OpenClaw uses OpenAI embeddings. Set
|
||||
`memory.search.provider` explicitly for Bedrock, DeepInfra, Gemini, GitHub
|
||||
Copilot, LM Studio, local, Mistral, Ollama, Voyage, or OpenAI-compatible
|
||||
embeddings. If the configured provider cannot run, `memory_search` may
|
||||
degrade to lexical-only retrieval; runtime failures after a provider is
|
||||
already selected do not fall back automatically.
|
||||
|
||||
Set an optional `memorySearch.fallback` only when you want a deliberate
|
||||
Set an optional `memory.search.fallback` only when you want a deliberate
|
||||
single fallback. See [Memory Search](/concepts/memory-search) for the full
|
||||
list of providers and examples.
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ this order:
|
||||
|
||||
1. **Model-scoped runtime policy** wins. This lives in a configured provider
|
||||
model entry, or in `agents.defaults.models["provider/model"].agentRuntime`
|
||||
/ `agents.list[].models["provider/model"].agentRuntime`. A provider
|
||||
/ `agents.entries.*.models["provider/model"].agentRuntime`. A provider
|
||||
wildcard such as `agents.defaults.models["vllm/*"].agentRuntime` applies
|
||||
after exact model policy, so dynamically discovered provider models can
|
||||
share one runtime without overriding exact per-model exceptions.
|
||||
@@ -145,7 +145,7 @@ this order:
|
||||
|
||||
Whole-session and whole-agent runtime pins are ignored: `OPENCLAW_AGENT_RUNTIME`,
|
||||
session `agentHarnessId`/`agentRuntimeOverride` state, `agents.defaults.agentRuntime`,
|
||||
and `agents.list[].agentRuntime`. Run `openclaw doctor --fix` to remove stale
|
||||
and `agents.entries.*.agentRuntime`. Run `openclaw doctor --fix` to remove stale
|
||||
whole-agent runtime config and convert legacy runtime model refs where intent
|
||||
can be preserved.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ When sandboxing is enabled and `workspaceAccess` is not `"rw"`, tools operate in
|
||||
- Default: `~/.openclaw/workspace`
|
||||
- If `OPENCLAW_PROFILE` is set and not `"default"`, the default becomes `~/.openclaw/workspace-<profile>`.
|
||||
- `OPENCLAW_WORKSPACE_DIR` overrides both of the above when set.
|
||||
- Non-default agents (`agents.list[]`) without an explicit workspace resolve to `<state-dir>/workspace-<agentId>`, not the shared default workspace.
|
||||
- Non-default agents (`agents.entries.*`) without an explicit workspace resolve to `<state-dir>/workspace-<agentId>`, not the shared default workspace.
|
||||
|
||||
Override in `~/.openclaw/openclaw.json`:
|
||||
|
||||
@@ -37,7 +37,7 @@ Override in `~/.openclaw/openclaw.json`:
|
||||
}
|
||||
```
|
||||
|
||||
Per-agent override: `agents.list[].workspace`.
|
||||
Per-agent override: `agents.entries.*.workspace`.
|
||||
|
||||
`openclaw onboard`, `openclaw configure`, or `openclaw setup` create the workspace and seed the bootstrap files if they are missing.
|
||||
|
||||
@@ -233,7 +233,7 @@ Suggested `.gitignore` starter:
|
||||
|
||||
## Advanced notes
|
||||
|
||||
- Multi-agent routing can use different workspaces per agent via `agents.list[].workspace`. See [Channel routing](/channels/channel-routing) for routing configuration.
|
||||
- Multi-agent routing can use different workspaces per agent via `agents.entries.*.workspace`. See [Channel routing](/channels/channel-routing) for routing configuration.
|
||||
- If `agents.defaults.sandbox` is enabled, non-main sessions can use per-session sandbox workspaces under `agents.defaults.sandbox.workspaceRoot`.
|
||||
|
||||
## Related
|
||||
|
||||
@@ -15,7 +15,7 @@ contain, which files get injected, and how sessions bootstrap against it.
|
||||
## Workspace (required)
|
||||
|
||||
Each agent uses a single workspace directory (`agents.defaults.workspace`, or
|
||||
`agents.list[].workspace` per agent) as its **only** working directory (`cwd`)
|
||||
`agents.entries.*.workspace` per agent) as its **only** working directory (`cwd`)
|
||||
for tools and context.
|
||||
|
||||
Recommended: use `openclaw setup` to create `~/.openclaw/openclaw.json` if missing and initialize the workspace files.
|
||||
|
||||
+17
-133
@@ -1,152 +1,36 @@
|
||||
---
|
||||
summary: "Inferred follow-up memory for check-ins that are not exact reminders"
|
||||
summary: "Status and cleanup guidance for retired inferred follow-up commitments"
|
||||
title: "Inferred commitments"
|
||||
sidebarTitle: "Commitments"
|
||||
read_when:
|
||||
- You want OpenClaw to remember natural follow-ups
|
||||
- You want to understand how inferred check-ins differ from reminders
|
||||
- You want to review or dismiss follow-up commitments
|
||||
- You are upgrading a configuration that used inferred commitments
|
||||
- You want to inspect or dismiss previously stored follow-up records
|
||||
---
|
||||
|
||||
Commitments are short-lived follow-up memories. When enabled, OpenClaw can
|
||||
notice that a conversation created a future check-in opportunity and remember
|
||||
to bring it back later.
|
||||
The inferred commitments experiment is retired. OpenClaw no longer extracts new
|
||||
conversation follow-ups or delivers them through heartbeat, and the former
|
||||
`commitments` config block is removed by `openclaw doctor --fix`.
|
||||
|
||||
Examples:
|
||||
Exact reminders and scheduled work continue to use
|
||||
[scheduled tasks](/automation/cron-jobs). Durable conversational facts belong in
|
||||
[memory](/concepts/memory).
|
||||
|
||||
- You mention an interview tomorrow. OpenClaw may check in afterward.
|
||||
- You say you are exhausted. OpenClaw may ask later whether you slept.
|
||||
- The agent says it will follow up after something changes. OpenClaw may track
|
||||
that open loop.
|
||||
## Existing records
|
||||
|
||||
Commitments are not durable facts like `MEMORY.md`, and they are not exact
|
||||
reminders. They sit between memory and automation: OpenClaw remembers a
|
||||
conversation-bound obligation, then heartbeat delivers it when it is due.
|
||||
|
||||
## Enable commitments
|
||||
|
||||
Commitments are off by default (`commitments.enabled: false`). Enable them in config:
|
||||
Previously stored commitments remain in the shared SQLite state database so an
|
||||
upgrade does not destroy operator-visible history. Use the legacy maintenance
|
||||
CLI to inspect or dismiss those rows:
|
||||
|
||||
```bash
|
||||
openclaw config set commitments.enabled true
|
||||
openclaw config set commitments.maxPerDay 3
|
||||
```
|
||||
|
||||
Equivalent `openclaw.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"commitments": {
|
||||
"enabled": true,
|
||||
"maxPerDay": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`commitments.maxPerDay` limits how many inferred follow-ups can be delivered
|
||||
per agent session in a rolling day. The default is `3`.
|
||||
|
||||
## How it works
|
||||
|
||||
After an agent reply, OpenClaw may run a hidden background extraction pass in a
|
||||
separate context, with tools disabled. That pass looks only for inferred follow-up commitments. It
|
||||
does not write into the visible conversation and it does not ask the main agent
|
||||
to reason about the extraction.
|
||||
|
||||
When it finds a high-confidence candidate, OpenClaw stores a commitment with:
|
||||
|
||||
- the agent id
|
||||
- the session key
|
||||
- the original channel and delivery target
|
||||
- a due window
|
||||
- a short suggested check-in
|
||||
- non-instructional metadata for heartbeat to decide whether to send it
|
||||
|
||||
Delivery happens through heartbeat. When a commitment becomes due, heartbeat
|
||||
adds the commitment to the heartbeat turn for the same agent and channel scope.
|
||||
The prompt explicitly warns that commitment metadata is untrusted and instructs
|
||||
the model not to follow instructions in it or use tools because of it. The
|
||||
model can send one natural check-in or reply `HEARTBEAT_OK` to dismiss it.
|
||||
If heartbeat is configured with `target: "none"`, due commitments remain
|
||||
internal and do not send external check-ins. Commitment delivery prompts do not
|
||||
replay the original conversation text, only the suggested check-in and
|
||||
metadata, and due-commitment heartbeat turns run without OpenClaw tools.
|
||||
|
||||
OpenClaw never delivers an inferred commitment immediately after writing it.
|
||||
The due time is clamped to at least one heartbeat interval after the commitment
|
||||
is created, so the follow-up cannot echo back in the same moment it was
|
||||
inferred.
|
||||
|
||||
## Scope
|
||||
|
||||
Commitments are scoped to the exact agent and channel context where they were
|
||||
created. A follow-up inferred while talking to one agent in Discord is not
|
||||
delivered by another agent, another channel, or an unrelated session.
|
||||
|
||||
This scope is part of the feature. Natural check-ins should feel like the same
|
||||
conversation continuing, not like a global reminder system.
|
||||
|
||||
## Commitments vs reminders
|
||||
|
||||
| Need | Use |
|
||||
| ----------------------------------------------- | ---------------------------------------- |
|
||||
| "Remind me at 3 PM" | [Scheduled tasks](/automation/cron-jobs) |
|
||||
| "Ping me in 20 minutes" | [Scheduled tasks](/automation/cron-jobs) |
|
||||
| "Run this report every weekday" | [Scheduled tasks](/automation/cron-jobs) |
|
||||
| "I have an interview tomorrow" | Commitments |
|
||||
| "I was up all night" | Commitments |
|
||||
| "Follow up if I do not answer this open thread" | Commitments |
|
||||
|
||||
Exact user requests already belong to the scheduler path. Commitments are only
|
||||
for inferred follow-ups: the moments where the user did not ask for a reminder,
|
||||
but the conversation clearly created a useful future check-in.
|
||||
|
||||
## Manage commitments
|
||||
|
||||
Use the CLI to inspect and clear stored commitments:
|
||||
|
||||
```bash
|
||||
openclaw commitments
|
||||
openclaw commitments --all
|
||||
openclaw commitments --agent main
|
||||
openclaw commitments --status snoozed
|
||||
openclaw commitments dismiss cm_abc123
|
||||
```
|
||||
|
||||
See [`openclaw commitments`](/cli/commitments) for the full command reference.
|
||||
|
||||
## Privacy and cost
|
||||
|
||||
Commitment extraction uses an LLM pass, so enabling it adds background model
|
||||
usage after eligible turns. The pass is hidden from the user-visible
|
||||
conversation, but it can read the recent exchange needed to decide whether a
|
||||
follow-up exists.
|
||||
|
||||
Stored commitments are local OpenClaw operational memory in the shared SQLite
|
||||
state database, not long-term memory. Disable the feature with:
|
||||
|
||||
```bash
|
||||
openclaw config set commitments.enabled false
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If expected follow-ups are not appearing:
|
||||
|
||||
- Confirm `commitments.enabled` is `true`.
|
||||
- Check `openclaw commitments --all` for pending, dismissed, snoozed, or expired
|
||||
records.
|
||||
- Make sure heartbeat is running for the agent.
|
||||
- Check whether `commitments.maxPerDay` has already been reached for that
|
||||
agent session.
|
||||
- Remember that exact reminders are skipped by commitment extraction and should
|
||||
appear under [scheduled tasks](/automation/cron-jobs) instead.
|
||||
See [`openclaw commitments`](/cli/commitments) for the maintenance command
|
||||
reference.
|
||||
|
||||
## Related
|
||||
|
||||
- [Memory overview](/concepts/memory)
|
||||
- [Active memory](/concepts/active-memory)
|
||||
- [Heartbeat](/gateway/heartbeat)
|
||||
- [Scheduled tasks](/automation/cron-jobs)
|
||||
- [`openclaw commitments`](/cli/commitments)
|
||||
- [Configuration reference](/gateway/configuration-reference#commitments)
|
||||
- [Memory overview](/concepts/memory)
|
||||
- [Heartbeat](/gateway/heartbeat)
|
||||
|
||||
@@ -100,7 +100,7 @@ When unset, compaction starts with the active session model. If summarization fa
|
||||
|
||||
### Identifier preservation
|
||||
|
||||
Compaction summarization preserves opaque identifiers by default (`identifierPolicy: "strict"`). Override with `identifierPolicy: "off"` to disable, or `identifierPolicy: "custom"` plus `identifierInstructions` for custom guidance.
|
||||
Compaction summarization preserves opaque identifiers by default (`identifierPolicy: "strict"`). Override with `identifierPolicy: "off"` to disable. Custom guidance belongs in a compaction provider's `summarize()` implementation.
|
||||
|
||||
### Active transcript byte guard
|
||||
|
||||
|
||||
@@ -16,14 +16,13 @@ Experimental features are preview surfaces behind explicit flags. They need more
|
||||
|
||||
## Currently documented flags
|
||||
|
||||
| Surface | Key | Use it when | More |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| Local model runtime | `agents.defaults.experimental.localModelLean`, `agents.list[].experimental.localModelLean` | A smaller or stricter local backend chokes on OpenClaw's full default tool surface | [Local Models](/gateway/local-models) |
|
||||
| Memory search | `agents.defaults.memorySearch.experimental.sessionMemory` | You want `memory_search` to index prior session transcripts and accept the extra storage/indexing cost | [Memory configuration reference](/reference/memory-config#session-memory-search-experimental) |
|
||||
| Codex harness | `plugins.entries.codex.config.appServer.experimental.sandboxExecServer` | You want native Codex app-server 0.132.0 or newer to target an OpenClaw sandbox-backed exec-server instead of disabling Code Mode | [Codex harness reference](/plugins/codex-harness-reference#sandboxed-native-execution) |
|
||||
| Structured planning tool | `tools.experimental.planTool` | You want the structured `update_plan` tool exposed for multi-step work tracking in compatible runtimes and UIs | [Gateway configuration reference](/gateway/config-tools#toolsexperimental) |
|
||||
| Code Mode | `tools.codeMode.enabled` | You want compact code-orchestrated access to a hidden OpenClaw tool catalog | [Code Mode](/tools/code-mode) |
|
||||
| Swarm | `tools.swarm.enabled` | You want Code Mode scripts to orchestrate bounded groups of sub-agents in parallel | [Swarm](/tools/swarm) |
|
||||
| Surface | Key | Use it when | More |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| Local model runtime | `agents.defaults.experimental.localModelLean`, `agents.entries.*.experimental.localModelLean` | A smaller or stricter local backend chokes on OpenClaw's full default tool surface | [Local Models](/gateway/local-models) |
|
||||
| Codex harness | `plugins.entries.codex.config.appServer.experimental.sandboxExecServer` | You want native Codex app-server 0.132.0 or newer to target an OpenClaw sandbox-backed exec-server instead of disabling Code Mode | [Codex harness reference](/plugins/codex-harness-reference#sandboxed-native-execution) |
|
||||
| Structured planning tool | `tools.experimental.planTool` | You want the structured `update_plan` tool exposed for multi-step work tracking in compatible runtimes and UIs | [Gateway configuration reference](/gateway/config-tools#toolsexperimental) |
|
||||
| Code Mode | `tools.codeMode.enabled` | You want compact code-orchestrated access to a hidden OpenClaw tool catalog | [Code Mode](/tools/code-mode) |
|
||||
| Swarm | `tools.swarm.enabled` | You want Code Mode scripts to orchestrate bounded groups of sub-agents in parallel | [Swarm](/tools/swarm) |
|
||||
|
||||
## Control UI Labs
|
||||
|
||||
@@ -38,7 +37,7 @@ runs without restarting the Gateway.
|
||||
|
||||
## Local model lean mode
|
||||
|
||||
`agents.defaults.experimental.localModelLean: true` drops heavyweight optional tools from the agent's direct surface every turn: `browser`, `cron`, `message`, `image_generate`, `music_generate`, `video_generate`, `tts`, and `pdf`. Explicitly allowed or delivery-required tools remain available, though Tool Search may catalog them instead of exposing them directly. Lean mode also defaults plugin/MCP/client catalogs to structured Tool Search (`tool_search`, `tool_describe`, `tool_call`) when `tools.toolSearch` is not already set. Use `agents.list[].experimental.localModelLean` to scope this to one agent.
|
||||
`agents.defaults.experimental.localModelLean: true` drops heavyweight optional tools from the agent's direct surface every turn: `browser`, `cron`, `message`, `image_generate`, `music_generate`, `video_generate`, `tts`, and `pdf`. Explicitly allowed or delivery-required tools remain available, though Tool Search may catalog them instead of exposing them directly. Lean mode also defaults plugin/MCP/client catalogs to structured Tool Search (`tool_search`, `tool_describe`, `tool_call`) when `tools.toolSearch` is not already set. Use `agents.entries.*.experimental.localModelLean` to scope this to one agent.
|
||||
|
||||
During onboarding, a verified `ollama` or `lmstudio` inference route automatically sets `agents.defaults.experimental.localModelLean: true` when that value is absent. OpenClaw records that the setting came from onboarding, so a later verified non-local route lifts only the automatic setting. An explicitly configured `true` or `false` is preserved. Other self-hosted and OpenAI-compatible providers are not inferred from model names or URLs.
|
||||
|
||||
|
||||
@@ -28,11 +28,9 @@ To set a provider explicitly:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
},
|
||||
memory: {
|
||||
search: {
|
||||
provider: "openai",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -49,14 +47,12 @@ openclaw plugins install @openclaw/llama-cpp-provider
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "local",
|
||||
fallback: "none",
|
||||
local: {
|
||||
modelPath: "~/.node-llama-cpp/models/embeddinggemma-300m-qat-Q8_0.gguf",
|
||||
},
|
||||
memory: {
|
||||
search: {
|
||||
provider: "local",
|
||||
fallback: "none",
|
||||
local: {
|
||||
modelPath: "~/.node-llama-cpp/models/embeddinggemma-300m-qat-Q8_0.gguf",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -79,7 +75,7 @@ openclaw plugins install @openclaw/llama-cpp-provider
|
||||
| OpenAI-compatible | `openai-compatible` | Generic `/v1/embeddings` endpoint |
|
||||
| Voyage | `voyage` | |
|
||||
|
||||
Set `memorySearch.provider` to switch away from OpenAI.
|
||||
Set `memory.search.provider` to switch away from OpenAI.
|
||||
|
||||
## How indexing works
|
||||
|
||||
@@ -98,7 +94,7 @@ OpenClaw indexes `MEMORY.md` and `memory/*.md` into chunks (400 tokens with
|
||||
|
||||
<Info>
|
||||
You can also index Markdown files outside the workspace with
|
||||
`memorySearch.extraPaths`. See the
|
||||
`memory.search.extraPaths`. See the
|
||||
[configuration reference](/reference/memory-config#additional-memory-paths).
|
||||
</Info>
|
||||
|
||||
@@ -130,7 +126,7 @@ openclaw memory index --force --agent main
|
||||
```
|
||||
|
||||
Both standalone CLI commands and the Gateway use the same `local` provider id.
|
||||
Set `memorySearch.provider: "local"` when you want local embeddings.
|
||||
Set `memory.search.provider: "local"` when you want local embeddings.
|
||||
|
||||
**Stale results?** Run `openclaw memory index --force` to rebuild. The watcher
|
||||
may miss changes in rare edge cases.
|
||||
|
||||
+12
-26
@@ -50,12 +50,9 @@ present.
|
||||
|
||||
## How the sidecar works
|
||||
|
||||
- OpenClaw creates collections from your workspace memory files and any
|
||||
configured `memory.qmd.paths`, then runs `qmd update` when the QMD manager
|
||||
opens and periodically afterward (`memory.qmd.update.interval`, default
|
||||
`5m`). Refreshes run through QMD subprocesses, not an in-process filesystem
|
||||
crawl. Semantic search modes also run `qmd embed`
|
||||
(`memory.qmd.update.embedInterval`, default `60m`).
|
||||
- OpenClaw creates collections from workspace memory files and configured
|
||||
`memory.qmd.paths`. The QMD adapter owns update, embedding, debounce, and
|
||||
timeout heuristics; these are not user configuration.
|
||||
- QMD continues to own its `index.sqlite`, YAML collection config, and model
|
||||
downloads under the per-agent QMD home; these are external-tool artifacts,
|
||||
not OpenClaw state tables. OpenClaw-owned coordination lives only in SQLite:
|
||||
@@ -71,15 +68,8 @@ present.
|
||||
tree. Lowercase `memory.md` is not indexed as a root memory file.
|
||||
- QMD's own scanner ignores hidden paths and common dependency/build
|
||||
directories such as `.git`, `.cache`, `node_modules`, `vendor`, `dist`, and
|
||||
`build`. Gateway startup does not initialize QMD by default
|
||||
(`memory.qmd.update.startup` defaults to `off`), so cold boot avoids
|
||||
importing the memory runtime or creating the long-lived watcher before
|
||||
memory is first used.
|
||||
- Set `memory.qmd.update.startup` to `idle` or `immediate` to initialize QMD
|
||||
at gateway start anyway. `memory.qmd.update.onBoot` defaults to `true` and
|
||||
runs the initial refresh at startup; set it to `false` to skip that
|
||||
immediate refresh (the long-lived manager still opens when update or embed
|
||||
intervals are configured, so QMD keeps owning its regular watcher/timers).
|
||||
`build`. Gateway startup keeps QMD lazy; the manager initializes when memory
|
||||
is first used.
|
||||
- Searches use the configured `searchMode` (default: `search`; also supports
|
||||
`vsearch` and `query`). `search` is BM25-only, so OpenClaw skips semantic
|
||||
vector readiness probes and embedding maintenance in that mode. If a mode
|
||||
@@ -169,20 +159,16 @@ correct collection root.
|
||||
## Indexing session transcripts
|
||||
|
||||
Enable session indexing to recall earlier conversations. QMD needs both the
|
||||
general `memorySearch` session source and the QMD transcript exporter:
|
||||
general `memory.search` session source and the QMD transcript exporter:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
experimental: { sessionMemory: true },
|
||||
sources: ["memory", "sessions"],
|
||||
},
|
||||
},
|
||||
},
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
search: {
|
||||
experimental: { sessionMemory: true },
|
||||
sources: ["memory", "sessions"],
|
||||
},
|
||||
qmd: {
|
||||
sessions: { enabled: true },
|
||||
},
|
||||
@@ -192,8 +178,8 @@ general `memorySearch` session source and the QMD transcript exporter:
|
||||
|
||||
Transcripts export as sanitized User/Assistant turns into a dedicated QMD
|
||||
collection under `~/.openclaw/agents/<id>/qmd/sessions/`. Setting only
|
||||
`memorySearch.experimental.sessionMemory` does not export transcripts into
|
||||
QMD.
|
||||
`sources: ["sessions"]` does not export transcripts into QMD; also enable
|
||||
`rememberAcrossConversations` or explicit QMD session export.
|
||||
|
||||
Session hits are still filtered by
|
||||
[`tools.sessions.visibility`](/gateway/config-tools#toolssessions). The
|
||||
|
||||
@@ -18,11 +18,9 @@ explicitly:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai", // or "gemini", "voyage", "mistral", "bedrock", "local", "ollama", "lmstudio", "github-copilot", "openai-compatible"
|
||||
},
|
||||
memory: {
|
||||
search: {
|
||||
provider: "openai", // or "gemini", "voyage", "mistral", "bedrock", "local", "ollama", "lmstudio", "github-copilot", "openai-compatible"
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -132,14 +130,12 @@ different daily notes.
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
query: {
|
||||
hybrid: {
|
||||
mmr: { enabled: true },
|
||||
temporalDecay: { enabled: true },
|
||||
},
|
||||
memory: {
|
||||
search: {
|
||||
query: {
|
||||
hybrid: {
|
||||
mmr: { enabled: true },
|
||||
temporalDecay: { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -150,7 +146,7 @@ different daily notes.
|
||||
## Multimodal memory
|
||||
|
||||
With `gemini-embedding-2-preview`, you can index images and audio alongside
|
||||
Markdown. This only applies to files under `memorySearch.extraPaths`; default
|
||||
Markdown. This only applies to files under `memory.search.extraPaths`; default
|
||||
memory roots (`MEMORY.md`, `memory/*.md`) stay Markdown-only. Search queries
|
||||
remain text, but they match against visual and audio content. See
|
||||
[Memory configuration reference](/reference/memory-config#multimodal-memory-gemini)
|
||||
@@ -187,9 +183,8 @@ and `sources` alone do not export transcripts into QMD. See
|
||||
**Only keyword matches?** Your embedding provider may not be configured. Check
|
||||
`openclaw memory status --deep`.
|
||||
|
||||
**Local embeddings time out?** `ollama`, `lmstudio`, and `local` use a longer
|
||||
inline batch timeout by default. If the host is just slow, set
|
||||
`agents.defaults.memorySearch.sync.embeddingBatchTimeoutSeconds` and rerun
|
||||
**Local embeddings time out?** `ollama`, `lmstudio`, and `local` use longer
|
||||
provider-owned batch deadlines. Check provider health and rerun
|
||||
`openclaw memory index --force`.
|
||||
|
||||
**CJK text not found?** Rebuild the FTS index with
|
||||
|
||||
@@ -123,21 +123,20 @@ This is not a required schema for every memory; simple facts can stay concise.
|
||||
Use action-sensitive boundaries when losing timing, authority, expiry, or
|
||||
safe-to-act context could cause the agent to do the wrong thing later.
|
||||
|
||||
Use [commitments](/concepts/commitments) for inferred, short-lived follow-ups.
|
||||
Use [scheduled tasks](/automation/cron-jobs) for exact reminders, timed checks,
|
||||
and recurring work. Memory can still summarize the durable context around
|
||||
either path.
|
||||
and recurring work. Memory can still summarize the durable context around that
|
||||
work.
|
||||
|
||||
## Inferred commitments
|
||||
## Retired inferred commitments
|
||||
|
||||
Some future follow-ups are not durable facts. If you mention an interview
|
||||
tomorrow, the useful memory may be "check in after the interview," not "store
|
||||
this forever in `MEMORY.md`."
|
||||
|
||||
[Commitments](/concepts/commitments) are opt-in, short-lived follow-up
|
||||
memories for that case. OpenClaw infers them in a hidden background pass,
|
||||
scopes them to the same agent and channel, and delivers due check-ins through
|
||||
heartbeat. Explicit reminders still use [scheduled tasks](/automation/cron-jobs).
|
||||
The inferred commitments experiment is retired. OpenClaw no longer extracts or
|
||||
delivers those follow-ups. Use [scheduled tasks](/automation/cron-jobs) for
|
||||
future actions; the legacy `openclaw commitments` command remains available to
|
||||
inspect or dismiss existing stored rows.
|
||||
|
||||
## Memory tools
|
||||
|
||||
@@ -158,7 +157,7 @@ for any supported provider.
|
||||
|
||||
<Info>
|
||||
OpenClaw uses OpenAI embeddings by default. Set
|
||||
`agents.defaults.memorySearch.provider` explicitly to use Gemini, Voyage,
|
||||
`memory.search.provider` explicitly to use Gemini, Voyage,
|
||||
Mistral, Bedrock, DeepInfra, local GGUF, Ollama, LM Studio, GitHub Copilot, or
|
||||
a generic OpenAI-compatible endpoint.
|
||||
</Info>
|
||||
|
||||
@@ -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 prefixes live at `channels.<channel>.responsePrefix` and `channels.<channel>.accounts.<id>.responsePrefix`. Account values win. Doctor copies the global fallback into configured channel blocks when those canonical fields are unset; `messages.responsePrefix` remains as a fallback for implicit and custom channels.
|
||||
- Reply threading via `replyToMode` and per-channel defaults.
|
||||
|
||||
Details: [Configuration](/gateway/config-agents#messages) and channel docs.
|
||||
|
||||
@@ -46,7 +46,7 @@ Fallback execution is turn-local. The reply runner persists only fallback notice
|
||||
The selection source controls whether the fallback chain is allowed:
|
||||
|
||||
- **Configured default**: `agents.defaults.model.primary` uses `agents.defaults.model.fallbacks`.
|
||||
- **Agent primary**: `agents.list[].model` is strict unless that agent's model object includes its own `fallbacks`. Use `fallbacks: []` to make the strict behavior explicit, or a non-empty list to opt that agent into model fallback.
|
||||
- **Agent primary**: `agents.entries.*.model` is strict unless that agent's model object includes its own `fallbacks`. Use `fallbacks: []` to make the strict behavior explicit, or a non-empty list to opt that agent into model fallback.
|
||||
- **Runtime fallback**: the fallback candidate applies only to the current turn. The next turn starts from the selected primary again. OpenClaw still recognizes previously stored `modelOverrideSource: "auto"` entries, probes their configured origin every 5 minutes, and clears them once the origin recovers. `/new`, `/reset`, and `sessions.reset` also clear those entries.
|
||||
- **User session override**: `/model`, the model picker, `session_status(model=...)`, and `sessions.patch` write `modelOverrideSource: "user"`. This is an exact session selection. If the selected provider/model fails before producing a reply, OpenClaw reports the failure instead of answering from an unrelated configured fallback.
|
||||
- **Legacy session override**: older session entries may have `modelOverride` without `modelOverrideSource`. OpenClaw treats those as user overrides so an explicit old selection is not silently converted into fallback behavior.
|
||||
|
||||
@@ -56,12 +56,12 @@ OpenAI API-key and ChatGPT/Codex subscription credentials remain distinct. See
|
||||
Related model-config surfaces:
|
||||
|
||||
- `agents.defaults.models` stores aliases and per-model settings. Adding an entry does not restrict model overrides.
|
||||
- `agents.defaults.modelPolicy.allow` is the optional override allowlist. Use exact refs or trailing prefix wildcards such as `provider/*` and `provider/namespace/*`; omit it or set `[]` to allow any model. Per-agent `agents.list[].modelPolicy.allow` replaces the default policy for that agent.
|
||||
- `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.list[].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. Generated titles retry once with the primary model when a distinct utility model fails. For dashboard titles, automatic utility derivation and the regular fallback follow the effective session provider and auth profile; an explicit utility model keeps its configured provider/auth. An empty utility model skips only the alternate small-model route, not dashboard title generation. Utility tasks are separate model calls and may send bounded task content to the selected model provider.
|
||||
- `agents.defaults.modelPolicy.allow` is the optional override allowlist. Use exact refs or trailing prefix wildcards such as `provider/*` and `provider/namespace/*`; omit it or set `[]` to allow any model. Per-agent `agents.entries.*.modelPolicy.allow` replaces the default policy for that agent.
|
||||
- `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. Generated titles retry once with the primary model when a distinct utility model fails. For dashboard titles, automatic utility derivation and the regular fallback follow the effective session provider and auth profile; an explicit utility model keeps its configured provider/auth. An empty utility model skips only the alternate small-model route, not dashboard title generation. 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.
|
||||
- Per-agent `agents.list[].model` (plus bindings) overrides `agents.defaults.model` — see [Multi-agent routing](/concepts/multi-agent).
|
||||
- `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).
|
||||
|
||||
@@ -108,7 +108,7 @@ Reauthentication preserves an existing explicit primary model, including
|
||||
|
||||
## "Model is not allowed" (and why replies stop)
|
||||
|
||||
If `agents.defaults.modelPolicy.allow` is non-empty, it becomes the allowlist for `/model`, session overrides, and `--model`. Selecting a model outside that allowlist returns before any normal reply is generated. A per-agent `agents.list[].modelPolicy.allow` replaces the default policy for that agent.
|
||||
If `agents.defaults.modelPolicy.allow` is non-empty, it becomes the allowlist for `/model`, session overrides, and `--model`. Selecting a model outside that allowlist returns before any normal reply is generated. A per-agent `agents.entries.*.modelPolicy.allow` replaces the default policy for that agent.
|
||||
|
||||
```text
|
||||
Model override "provider/model" is not allowed by agents.defaults.modelPolicy.allow.
|
||||
|
||||
@@ -32,7 +32,7 @@ Auth profiles are per-agent, read from:
|
||||
Never reuse `agentDir` across agents — it causes auth/session state collisions. When a secondary agent's local OAuth credential is expired or its refresh fails, OpenClaw reads through to the default/main agent's credential for the same profile id and adopts whichever token is freshest, without copying the refresh token into the secondary agent's store. If you want a fully independent OAuth account, sign in from that agent. If you copy credentials manually, copy only portable static `api_key` or `token` profiles — OAuth refresh material is not portable by default (`copyToAgents` can opt a profile in explicitly).
|
||||
</Warning>
|
||||
|
||||
Skills load from each agent workspace plus shared roots such as `~/.openclaw/skills`, then filter by the effective agent skill allowlist. Use `agents.defaults.skills` for a shared baseline and `agents.list[].skills` for a per-agent replacement (explicit entries replace the default, they do not merge). See [Skills: per-agent vs shared](/tools/skills#per-agent-vs-shared-skills) and [Skills: agent allowlists](/tools/skills#agent-allowlists).
|
||||
Skills load from each agent workspace plus shared roots such as `~/.openclaw/skills`, then filter by the effective agent skill allowlist. Use `agents.defaults.skills` for a shared baseline and `agents.entries.*.skills` for a per-agent replacement (explicit entries replace the default, they do not merge). See [Skills: per-agent vs shared](/tools/skills#per-agent-vs-shared-skills) and [Skills: agent allowlists](/tools/skills#agent-allowlists).
|
||||
|
||||
Plugin-owned storage follows that plugin's configuration; adding a second agent
|
||||
does not automatically split every global plugin store. For example, configure
|
||||
@@ -45,15 +45,15 @@ when personas must not share compiled wiki knowledge.
|
||||
|
||||
## Paths
|
||||
|
||||
| What | Default | Override |
|
||||
| -------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| Config | `~/.openclaw/openclaw.json` | `OPENCLAW_CONFIG_PATH` |
|
||||
| State dir | `~/.openclaw` | `OPENCLAW_STATE_DIR` |
|
||||
| Default agent's workspace | `~/.openclaw/workspace` (or `workspace-<profile>` when `OPENCLAW_PROFILE` is set) | `agents.list[].workspace`, then `agents.defaults.workspace`, or `OPENCLAW_WORKSPACE_DIR` |
|
||||
| Other agents' workspace | `<stateDir>/workspace-<agentId>` (or `<agents.defaults.workspace>/<agentId>` when set) | `agents.list[].workspace` |
|
||||
| Agent dir | `~/.openclaw/agents/<agentId>/agent` | `agents.list[].agentDir` |
|
||||
| Sessions and transcripts | `~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite` | — |
|
||||
| Legacy/archive session artifacts | `~/.openclaw/agents/<agentId>/sessions` | — |
|
||||
| What | Default | Override |
|
||||
| -------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| Config | `~/.openclaw/openclaw.json` | `OPENCLAW_CONFIG_PATH` |
|
||||
| State dir | `~/.openclaw` | `OPENCLAW_STATE_DIR` |
|
||||
| Default agent's workspace | `~/.openclaw/workspace` (or `workspace-<profile>` when `OPENCLAW_PROFILE` is set) | `agents.entries.*.workspace`, then `agents.defaults.workspace`, or `OPENCLAW_WORKSPACE_DIR` |
|
||||
| Other agents' workspace | `<stateDir>/workspace-<agentId>` (or `<agents.defaults.workspace>/<agentId>` when set) | `agents.entries.*.workspace` |
|
||||
| Agent dir | `~/.openclaw/agents/<agentId>/agent` | `agents.entries.*.agentDir` |
|
||||
| Sessions and transcripts | `~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite` | — |
|
||||
| Legacy/archive session artifacts | `~/.openclaw/agents/<agentId>/sessions` | — |
|
||||
|
||||
### Single-agent mode (default)
|
||||
|
||||
@@ -107,7 +107,7 @@ openclaw agents list --bindings
|
||||
|
||||
</Step>
|
||||
<Step title="Add agents, accounts, and bindings">
|
||||
Add agents under `agents.list`, channel accounts under `channels.<channel>.accounts`, and connect them with `bindings` (examples below).
|
||||
Add agents under `agents.entries`, channel accounts under `channels.<channel>.accounts`, and connect them with `bindings` (examples below).
|
||||
</Step>
|
||||
<Step title="Restart and verify">
|
||||
```bash
|
||||
@@ -161,34 +161,35 @@ filtering, migration, and trust-boundary details.
|
||||
|
||||
## Cross-agent QMD memory search
|
||||
|
||||
To let one agent search another agent's QMD session transcripts, add extra collections under `agents.list[].memorySearch.qmd.extraCollections`. Use `agents.defaults.memorySearch.qmd.extraCollections` when every agent should share the same collections.
|
||||
To let one agent search another agent's QMD session transcripts, add extra collections under `agents.entries.*.memory.search.qmd.extraCollections`. Use `memory.search.qmd.extraCollections` when every agent should share the same collections.
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: "~/workspaces/main",
|
||||
memorySearch: {
|
||||
qmd: {
|
||||
extraCollections: [{ path: "~/agents/family/sessions", name: "family-sessions" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
entries: {
|
||||
main: {
|
||||
workspace: "~/workspaces/main",
|
||||
memorySearch: {
|
||||
qmd: {
|
||||
extraCollections: [{ path: "notes" }], // resolves inside workspace -> collection named "notes-main"
|
||||
memory: {
|
||||
search: {
|
||||
qmd: {
|
||||
extraCollections: [{ path: "notes" }], // resolves inside workspace -> collection named "notes-main"
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ id: "family", workspace: "~/workspaces/family" },
|
||||
],
|
||||
family: { workspace: "~/workspaces/family" },
|
||||
},
|
||||
},
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
search: {
|
||||
qmd: {
|
||||
extraCollections: [{ path: "~/agents/family/sessions", name: "family-sessions" }],
|
||||
},
|
||||
},
|
||||
qmd: { includeDefaultMemory: false },
|
||||
},
|
||||
}
|
||||
@@ -536,7 +537,7 @@ Channels supporting multiple accounts: `discord`, `feishu`, `googlechat`, `imess
|
||||
}
|
||||
```
|
||||
|
||||
Tool allow/deny lists are **tools**, not skills. If a skill needs to run a binary, ensure `exec` is allowed and the binary exists in the sandbox. For stricter gating, set `agents.list[].groupChat.mentionPatterns` and keep group allowlists enabled for the channel.
|
||||
Tool allow/deny lists are **tools**, not skills. If a skill needs to run a binary, ensure `exec` is allowed and the binary exists in the sandbox. For stricter gating, set `agents.entries.*.groupChat.mentionPatterns` and keep group allowlists enabled for the channel.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -589,7 +590,7 @@ This gives you:
|
||||
- **Flexible policies**: different permissions per agent.
|
||||
|
||||
<Note>
|
||||
`tools.elevated` has both a global gate (`tools.elevated.enabled`/`allowFrom`) and a per-agent gate (`agents.list[].tools.elevated.enabled`/`allowFrom`). The per-agent gate can only further restrict the global one — both must allow a sender for elevated commands to run. For group targeting, use `agents.list[].groupChat.mentionPatterns` so @mentions map cleanly to the intended agent.
|
||||
`tools.elevated` has both a global gate (`tools.elevated.enabled`/`allowFrom`) and a per-agent gate (`agents.entries.*.tools.elevated.enabled`/`allowFrom`). The per-agent gate can only further restrict the global one — both must allow a sender for elevated commands to run. For group targeting, use `agents.entries.*.groupChat.mentionPatterns` so @mentions map cleanly to the intended agent.
|
||||
</Note>
|
||||
|
||||
See [Multi-agent sandbox and tools](/tools/multi-agent-sandbox-tools) for detailed examples.
|
||||
|
||||
@@ -71,7 +71,7 @@ Verify your setup with `openclaw security audit`.
|
||||
## Remember across conversations
|
||||
|
||||
Separate transcripts control each conversation's local history. For a personal
|
||||
or fully trusted agent, `memorySearch.rememberAcrossConversations: true`
|
||||
or fully trusted agent, `memory.search.rememberAcrossConversations: true`
|
||||
adds an optional retrieval step across that agent's other private
|
||||
conversations; it does not combine their transcripts.
|
||||
|
||||
@@ -134,9 +134,7 @@ Opt into automatic resets globally, then override them per chat type or channel:
|
||||
}
|
||||
```
|
||||
|
||||
`resetByType` supports `direct` (legacy alias `dm`), `group`, and `thread`.
|
||||
Legacy top-level `session.idleMinutes` still works as a compatibility alias for
|
||||
an idle-mode default when no `session.reset`/`resetByType` block is set.
|
||||
`resetByType` supports `direct`, `group`, and `thread`. Doctor migrates legacy `dm` entries to `direct` and `session.idleMinutes` to `session.reset.idleMinutes`; the schema rejects both retired forms.
|
||||
|
||||
## Where state lives
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ replies, after the first block, so multi-bubble responses feel more natural.
|
||||
| `natural` | 800-2500ms random pause |
|
||||
| `custom` | `minMs`/`maxMs` |
|
||||
|
||||
Override per agent via `agents.list[].humanDelay`. Applies only to **block
|
||||
Override per agent via `agents.entries.*.humanDelay`. Applies only to **block
|
||||
replies**, not final replies or tool summaries.
|
||||
|
||||
## "Stream chunks or everything"
|
||||
|
||||
@@ -35,7 +35,7 @@ The prompt is compact, with fixed sections:
|
||||
- **Safety**: short guardrail reminder against power-seeking behavior or bypassing oversight.
|
||||
- **Skills** (when available): tells the model how to load skill instructions on demand.
|
||||
- **OpenClaw Control**: prefer the `gateway` tool for config/restart work; do not invent CLI commands.
|
||||
- **OpenClaw Self-Update**: inspect config safely with `config.schema.lookup`, patch with `config.patch`, replace the full config with `config.apply`, and run `update.run` only on explicit user request. The agent-facing `gateway` tool refuses to rewrite `tools.exec.ask` / `tools.exec.security`, including legacy `tools.bash.*` aliases that normalize to those protected paths.
|
||||
- **OpenClaw Self-Update**: inspect config safely with `config.schema.lookup`, patch with `config.patch`, replace the full config with `config.apply`, and run `update.run` only on explicit user request. The agent-facing `gateway` tool refuses to rewrite `tools.exec.mode`.
|
||||
- **Workspace**: working directory (`agents.defaults.workspace`).
|
||||
- **Documentation**: local docs/source path and when to read them.
|
||||
- **Workspace Files (injected)**: notes that bootstrap files are included below.
|
||||
@@ -147,7 +147,7 @@ Native Codex turns receive this list as turn-scoped collaboration developer inst
|
||||
|
||||
The location can point at a nested skill, such as `skills/personal/foo/SKILL.md`. Nesting is only organizational; the prompt uses the flat skill name from `SKILL.md` frontmatter.
|
||||
|
||||
Eligibility includes skill metadata gates, runtime environment/config checks, and the effective agent skill allowlist when `agents.defaults.skills` or `agents.list[].skills` is configured. Plugin-bundled skills are eligible only when their owning plugin is enabled, letting tool plugins expose deeper operating guides without embedding all of that guidance in every tool description.
|
||||
Eligibility includes skill metadata gates, runtime environment/config checks, and the effective agent skill allowlist when `agents.defaults.skills` or `agents.entries.*.skills` is configured. Plugin-bundled skills are eligible only when their owning plugin is enabled, letting tool plugins expose deeper operating guides without embedding all of that guidance in every tool description.
|
||||
|
||||
```xml
|
||||
<available_skills>
|
||||
@@ -162,10 +162,10 @@ Eligibility includes skill metadata gates, runtime environment/config checks, an
|
||||
|
||||
This keeps the base prompt small while still enabling targeted skill usage. Sizing is owned by the skills subsystem, separate from generic runtime read/injection sizing:
|
||||
|
||||
| Scope | Skills prompt budget | Runtime excerpt budget |
|
||||
| --------- | ------------------------------------------------- | --------------------------------- |
|
||||
| Global | `skills.limits.maxSkillsPromptChars` | `agents.defaults.contextLimits.*` |
|
||||
| Per-agent | `agents.list[].skillsLimits.maxSkillsPromptChars` | `agents.list[].contextLimits.*` |
|
||||
| Scope | Skills prompt budget | Runtime excerpt budget |
|
||||
| --------- | ---------------------------------------------------- | ---------------------------------- |
|
||||
| Global | `skills.limits.maxSkillsPromptChars` | `agents.defaults.contextLimits.*` |
|
||||
| Per-agent | `agents.entries.*.skillsLimits.maxSkillsPromptChars` | `agents.entries.*.contextLimits.*` |
|
||||
|
||||
The runtime excerpt budget covers `memory_get`, live tool results, and post-compaction `AGENTS.md` refreshes.
|
||||
|
||||
|
||||
@@ -42,12 +42,17 @@ Set the agent-level default:
|
||||
}
|
||||
```
|
||||
|
||||
Override the mode per session:
|
||||
Override the policy for one agent:
|
||||
|
||||
```json5
|
||||
{
|
||||
session: {
|
||||
typingMode: "message",
|
||||
agents: {
|
||||
entries: {
|
||||
support: {
|
||||
typingMode: "message",
|
||||
typingIntervalSeconds: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
@@ -58,7 +63,7 @@ Override the mode per session:
|
||||
- `thinking` still reacts to streamed reasoning (`reasoningLevel: "stream"`), and can also start from active execution before reasoning deltas arrive.
|
||||
- Heartbeat typing is a liveness signal for the resolved delivery target. It starts at heartbeat run start instead of following `message` or `thinking` stream timing. Set `typingMode: "never"` to disable it.
|
||||
- Heartbeats do not show typing when the heartbeat target is `"none"`, when the target cannot be resolved, when chat delivery is disabled for the heartbeat, or when the channel does not support typing.
|
||||
- `agents.defaults.typingIntervalSeconds` controls the **refresh cadence**, not the start time. Default: 6 seconds.
|
||||
- `agents.defaults.typingIntervalSeconds` controls the **refresh cadence**, not the start time. Default: 6 seconds. `agents.entries.*.typingIntervalSeconds` can override it per agent.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
+47
-51
@@ -143,7 +143,6 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Core concepts
|
||||
- H3: Scheduled tasks (cron)
|
||||
- H3: Tasks
|
||||
- H3: Inferred commitments
|
||||
- H3: Task Flow
|
||||
- H3: Standing orders
|
||||
- H3: Hooks
|
||||
@@ -384,8 +383,8 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: Restrict senders within a group
|
||||
- H3: Bot-authored messages
|
||||
- H2: Get group/user IDs
|
||||
- H3: Group IDs (chatid, format: ocxxx)
|
||||
- H3: User IDs (openid, format: ouxxx)
|
||||
- H3: Group IDs (`chat_id`, format: `oc_xxx`)
|
||||
- H3: User IDs (`open_id`, format: `ou_xxx`)
|
||||
- H2: Common commands
|
||||
- H2: Troubleshooting
|
||||
- H3: Bot does not respond in group chats
|
||||
@@ -1178,6 +1177,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Manual dispatches
|
||||
- H2: Runners
|
||||
- H2: Runner registration budget
|
||||
- H2: Surface ratchets
|
||||
- H2: Local equivalents
|
||||
- H2: OpenClaw Performance
|
||||
- H2: Full Release Validation
|
||||
@@ -1204,6 +1204,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: Test Performance Agent
|
||||
- H3: Duplicate PRs After Merge
|
||||
- H2: Local check gates and changed routing
|
||||
- H3: Config baseline count ratchet
|
||||
- H2: Testbox validation
|
||||
- H2: Related
|
||||
|
||||
@@ -1267,7 +1268,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Examples
|
||||
- H2: Command surface
|
||||
- H3: agents list
|
||||
- H3: agents add [name]
|
||||
- H3: `agents add [name]`
|
||||
- H3: agents bindings
|
||||
- H3: agents bind
|
||||
- H3: agents unbind
|
||||
@@ -1490,16 +1491,16 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Common options
|
||||
- H2: Commands
|
||||
- H3: openclaw devices list
|
||||
- H3: openclaw devices approve [requestId] [--latest]
|
||||
- H3: `openclaw devices approve [requestId] [--latest]`
|
||||
- H3: openclaw devices reject <requestId>
|
||||
- H3: openclaw devices remove <deviceId>
|
||||
- H3: openclaw devices rename --device <id> --name <label>
|
||||
- H3: openclaw devices clear --yes [--pending]
|
||||
- H3: openclaw devices rotate --device <id> --role <role> [--scope <scope...>]
|
||||
- H3: `openclaw devices clear --yes [--pending]`
|
||||
- H3: `openclaw devices rotate --device <id> --role <role> [--scope <scope...>]`
|
||||
- H3: openclaw devices revoke --device <id> --role <role>
|
||||
- H2: Notes
|
||||
- H2: Token drift recovery checklist
|
||||
- H2: Paperclip / openclawgateway first-run approval
|
||||
- H2: Paperclip / `openclaw_gateway` first-run approval
|
||||
- H2: Related
|
||||
|
||||
## cli/directory.md
|
||||
@@ -2370,13 +2371,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
|
||||
- Route: /concepts/commitments
|
||||
- Headings:
|
||||
- H2: Enable commitments
|
||||
- H2: How it works
|
||||
- H2: Scope
|
||||
- H2: Commitments vs reminders
|
||||
- H2: Manage commitments
|
||||
- H2: Privacy and cost
|
||||
- H2: Troubleshooting
|
||||
- H2: Existing records
|
||||
- H2: Related
|
||||
|
||||
## concepts/compaction.md
|
||||
@@ -2644,7 +2639,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: What goes where
|
||||
- H2: Import from coding assistants
|
||||
- H2: Action-sensitive memories
|
||||
- H2: Inferred commitments
|
||||
- H2: Retired inferred commitments
|
||||
- H2: Memory tools
|
||||
- H2: Memory search
|
||||
- H2: Memory backends
|
||||
@@ -3307,9 +3302,9 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: Context budget ownership map
|
||||
- H4: agents.defaults.startupContext
|
||||
- H4: agents.defaults.contextLimits
|
||||
- H4: agents.list[].contextLimits
|
||||
- H4: `agents.entries.*.contextLimits`
|
||||
- H4: skills.limits.maxSkillsPromptChars
|
||||
- H4: agents.list[].skillsLimits.maxSkillsPromptChars
|
||||
- H4: `agents.entries.*.skillsLimits.maxSkillsPromptChars`
|
||||
- H3: agents.defaults.imageMaxDimensionPx
|
||||
- H3: agents.defaults.imageQuality
|
||||
- H3: agents.defaults.userTimezone
|
||||
@@ -3324,7 +3319,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: Block streaming
|
||||
- H3: Typing indicators
|
||||
- H3: agents.defaults.sandbox
|
||||
- H3: agents.list (per-agent overrides)
|
||||
- H3: agents.entries (per-agent overrides)
|
||||
- H2: Multi-agent routing
|
||||
- H3: Binding match fields
|
||||
- H3: Per-agent access profiles
|
||||
@@ -3385,7 +3380,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: tools.media
|
||||
- H3: tools.agentToAgent
|
||||
- H3: tools.sessions
|
||||
- H3: tools.sessionsspawn
|
||||
- H3: `tools.sessions_spawn`
|
||||
- H3: tools.experimental
|
||||
- H3: agents.defaults.subagents
|
||||
- H2: Custom providers and base URLs
|
||||
@@ -3425,7 +3420,6 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Skills
|
||||
- H2: Plugins
|
||||
- H3: Codex harness plugin config
|
||||
- H2: Commitments
|
||||
- H2: Browser
|
||||
- H2: UI
|
||||
- H2: Gateway
|
||||
@@ -3455,7 +3449,6 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Diagnostics
|
||||
- H2: Update
|
||||
- H2: ACP
|
||||
- H2: CLI
|
||||
- H2: Wizard
|
||||
- H2: Identity
|
||||
- H2: Bridge (legacy, removed)
|
||||
@@ -3726,11 +3719,11 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Request shape
|
||||
- H2: Items (input)
|
||||
- H3: message
|
||||
- H3: functioncalloutput (turn-based tools)
|
||||
- H3: reasoning and itemreference
|
||||
- H3: `function_call_output` (turn-based tools)
|
||||
- H3: reasoning and `item_reference`
|
||||
- H2: Tools (client-side function tools)
|
||||
- H2: Images (inputimage)
|
||||
- H2: Files (inputfile)
|
||||
- H2: Images (`input_image`)
|
||||
- H2: Files (`input_file`)
|
||||
- H2: File + image limits
|
||||
- H2: Streaming (SSE)
|
||||
- H2: Usage
|
||||
@@ -4199,6 +4192,12 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- Route: /help/environment
|
||||
- Headings:
|
||||
- H2: Precedence (highest to lowest)
|
||||
- H2: Supported operator-facing variables
|
||||
- H3: Paths and instances
|
||||
- H3: Gateway and authentication
|
||||
- H3: Provider credentials
|
||||
- H3: Logging and diagnostics
|
||||
- H3: Feature and runtime toggles
|
||||
- H2: Provider credentials and workspace .env
|
||||
- H2: Config env block
|
||||
- H2: Shell env import
|
||||
@@ -4210,7 +4209,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Path-related env vars
|
||||
- H2: Agent helper tool downloads
|
||||
- H2: Logging
|
||||
- H3: OPENCLAWHOME
|
||||
- H3: `OPENCLAW_HOME`
|
||||
- H2: nvm users: webfetch TLS failures
|
||||
- H2: Legacy environment variables
|
||||
- H2: Related
|
||||
@@ -4916,7 +4915,6 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Auto-detection (default)
|
||||
- H2: Config examples
|
||||
- H3: Provider + CLI fallback (OpenAI + Whisper CLI)
|
||||
- H3: Provider-only with scope gating
|
||||
- H3: Provider-only (Deepgram)
|
||||
- H3: Provider-only (Mistral Voxtral)
|
||||
- H3: Provider-only (SenseAudio)
|
||||
@@ -5067,7 +5065,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- Headings:
|
||||
- H2: Behavior (macOS)
|
||||
- H2: Voice directives in replies
|
||||
- H2: Config (/.openclaw/openclaw.json)
|
||||
- H2: Config (`~/.openclaw/openclaw.json`)
|
||||
- H2: macOS UI
|
||||
- H2: Android UI
|
||||
- H2: Notes
|
||||
@@ -5159,10 +5157,10 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: 2. Decisions (maintainer, 2026-07-17)
|
||||
- H2: 3. Architecture overview
|
||||
- H2: 4. Config gate (v1)
|
||||
- H2: 5. Core: collector-mode spawn + agentswait (v1)
|
||||
- H3: 5.1 sessionsspawn additions (all gated on swarm enabled)
|
||||
- H2: 5. Core: collector-mode spawn + `agents_wait` (v1)
|
||||
- H3: 5.1 `sessions_spawn` additions (all gated on swarm enabled)
|
||||
- H3: 5.2 Approvals fail-closed
|
||||
- H3: 5.3 agentswait tool (new, gated)
|
||||
- H3: 5.3 `agents_wait` tool (new, gated)
|
||||
- H3: 5.4 Caps enforcement
|
||||
- H2: 6. Testing contract (v1, lane A)
|
||||
- H2: 7. QuickJS guest surface (lane B, after core)
|
||||
@@ -5267,7 +5265,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Troubleshooting
|
||||
- H3: Command is not declared by the node
|
||||
- H3: Command requires explicit opt-in
|
||||
- H3: HEALTHACCESSDISABLED
|
||||
- H3: `HEALTH_ACCESS_DISABLED`
|
||||
- H3: Summary succeeds but metrics are missing
|
||||
- H3: Older ranges fail
|
||||
- H2: Related
|
||||
@@ -7752,8 +7750,8 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Security model
|
||||
- H2: Request format
|
||||
- H2: Supported actions
|
||||
- H3: createflow
|
||||
- H3: runtask
|
||||
- H3: `create_flow`
|
||||
- H3: `run_task`
|
||||
- H2: Response shape
|
||||
- H2: Related
|
||||
|
||||
@@ -8827,7 +8825,6 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: API key resolution
|
||||
- H2: Remote endpoint config
|
||||
- H2: Provider-specific config
|
||||
- H3: Inline embedding timeout
|
||||
- H2: Indexing behavior
|
||||
- H2: Hybrid search config
|
||||
- H3: Full example
|
||||
@@ -8835,11 +8832,10 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Multimodal memory (Gemini)
|
||||
- H2: Embedding cache
|
||||
- H2: Batch indexing
|
||||
- H2: Session memory search (experimental)
|
||||
- H2: Session memory search
|
||||
- H2: SQLite vector acceleration (sqlite-vec)
|
||||
- H2: Index storage
|
||||
- H2: QMD backend config
|
||||
- H3: mcporter integration
|
||||
- H3: Full QMD example
|
||||
- H2: Dreaming
|
||||
- H3: User settings
|
||||
@@ -8929,7 +8925,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- Route: /reference/rich-output-protocol
|
||||
- Headings:
|
||||
- H2: Media attachments
|
||||
- H2: [embed ...]
|
||||
- H2: `[embed ...]`
|
||||
- H2: Stored rendering shape
|
||||
- H2: Related
|
||||
|
||||
@@ -8981,7 +8977,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Compaction settings
|
||||
- H2: Pluggable compaction providers
|
||||
- H2: User-visible surfaces
|
||||
- H2: Silent housekeeping (NOREPLY)
|
||||
- H2: Silent housekeeping (`NO_REPLY`)
|
||||
- H2: Pre-compaction memory flush
|
||||
- H2: Troubleshooting checklist
|
||||
- H2: Related
|
||||
@@ -9624,7 +9620,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: Example
|
||||
- H3: Behavior
|
||||
- H2: Start ACP sessions
|
||||
- H3: sessionsspawn parameters
|
||||
- H3: `sessions_spawn` parameters
|
||||
- H2: Spawn bind and thread modes
|
||||
- H2: Delivery model
|
||||
- H2: Sandbox compatibility
|
||||
@@ -10012,10 +10008,10 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Configure Firecrawl webfetch fallback
|
||||
- H3: Self-hosted Firecrawl
|
||||
- H2: Firecrawl plugin tools
|
||||
- H3: firecrawlsearch
|
||||
- H3: firecrawlscrape
|
||||
- H3: `firecrawl_search`
|
||||
- H3: `firecrawl_scrape`
|
||||
- H2: Stealth / bot circumvention
|
||||
- H2: How webfetch uses Firecrawl
|
||||
- H2: How `web_fetch` uses Firecrawl
|
||||
- H2: Related
|
||||
|
||||
## tools/gemini-search.md
|
||||
@@ -10447,11 +10443,11 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: Thread binding controls
|
||||
- H3: Spawn behavior
|
||||
- H2: Context modes
|
||||
- H2: Tool: sessionsspawn
|
||||
- H2: Tool: `sessions_spawn`
|
||||
- H3: Delegation prompt mode
|
||||
- H3: Tool parameters
|
||||
- H3: Task names and targeting
|
||||
- H2: Tool: sessionsyield
|
||||
- H2: Tool: `sessions_yield`
|
||||
- H2: Tool: subagents
|
||||
- H2: Thread-bound sessions
|
||||
- H3: Thread supporting channels
|
||||
@@ -10471,7 +10467,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Announce
|
||||
- H3: Announce context
|
||||
- H3: Stats line
|
||||
- H3: Why prefer sessionshistory
|
||||
- H3: Why prefer `sessions_history`
|
||||
- H2: Tool policy
|
||||
- H3: Override via config
|
||||
- H2: Concurrency
|
||||
@@ -10503,8 +10499,8 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- Headings:
|
||||
- H2: Getting started
|
||||
- H2: Tool reference
|
||||
- H3: tavilysearch
|
||||
- H3: tavilyextract
|
||||
- H3: `tavily_search`
|
||||
- H3: `tavily_extract`
|
||||
- H2: Choosing the right tool
|
||||
- H2: Advanced configuration
|
||||
- H2: Related
|
||||
@@ -10576,9 +10572,9 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: Per-agent voice overrides
|
||||
- H2: Personas
|
||||
- H3: Minimal persona
|
||||
- H3: Full persona (provider-neutral prompt)
|
||||
- H3: Full persona (provider-specific shaping)
|
||||
- H3: Persona resolution
|
||||
- H3: How providers use persona prompts
|
||||
- H3: Custom persona shaping
|
||||
- H3: Fallback policy
|
||||
- H2: Model-driven directives
|
||||
- H2: Slash commands
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -141,11 +141,11 @@ openclaw plugins enable bonjour
|
||||
|
||||
When enabled, Bonjour uses `discovery.mdns.mode` to decide how much TXT metadata to publish; the same mode controls optional TXT hints in wide-area DNS-SD records. Modes:
|
||||
|
||||
| Mode | Behavior |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `minimal` (default) | Core TXT keys only; omits `sshPort`, `cliPath`, `tailnetDns`. |
|
||||
| `full` | Adds `sshPort`, `cliPath`, `tailnetDns` — use when clients need those hints. |
|
||||
| `off` | Suppresses LAN multicast without changing plugin enablement; wide-area DNS-SD can still publish the minimal beacon when `discovery.wideArea.enabled` is true. |
|
||||
| Mode | Behavior |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `minimal` (default) | Core TXT keys only; omits `sshPort`, `cliPath`, `tailnetDns`. |
|
||||
| `full` | Adds `sshPort`, `cliPath`, `tailnetDns` — use when clients need those hints. |
|
||||
| `off` | Suppresses LAN multicast without changing plugin enablement; wide-area DNS-SD can still publish when `discovery.wideArea.domain` is set. |
|
||||
|
||||
## When to disable Bonjour
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ All CLI backends live under `agents.defaults.cliBackends`, keyed by provider id
|
||||
"claude-opus-4-6": "opus",
|
||||
"claude-sonnet-4-6": "sonnet",
|
||||
},
|
||||
sessionArg: "--session",
|
||||
sessionArgs: ["--session", "{sessionId}"],
|
||||
sessionMode: "existing",
|
||||
sessionIdFields: ["session_id", "conversation_id"],
|
||||
systemPromptArg: "--system",
|
||||
@@ -146,7 +146,7 @@ The `openclaw agent` command also has its own request deadline. Its 600-second f
|
||||
|
||||
The bundled `claude-cli` backend prefers Claude Code's native skill resolver. When the current skills snapshot has at least one selected skill with a materialized path, OpenClaw passes a temporary Claude Code plugin via `--plugin-dir` and omits the duplicate OpenClaw skills catalog from the appended system prompt. Without a materialized plugin skill, OpenClaw keeps the prompt catalog as a fallback. Skill env/API key overrides still apply to the child process environment for the run.
|
||||
|
||||
Claude CLI has its own noninteractive permission mode; OpenClaw maps that to the existing exec policy instead of adding Claude-specific config. For OpenClaw-managed Claude live sessions, the effective exec policy is authoritative: YOLO (`tools.exec.security: "full"` and `tools.exec.ask: "off"`) normally launches Claude with `--permission-mode bypassPermissions`, while a restrictive policy launches it with `--permission-mode default`. Root-run gateways also use `default` because Claude Code rejects bypass mode for root; OpenClaw still answers Claude's stdio tool-control requests from the configured exec policy. Per-agent `agents.list[].tools.exec` settings override the global `tools.exec` for that agent. Raw backend args may still include `--permission-mode`, but live Claude launches normalize that flag to match the effective policy and host restriction.
|
||||
Claude CLI has its own noninteractive permission mode; OpenClaw maps that to the existing exec policy instead of adding Claude-specific config. For OpenClaw-managed Claude live sessions, the effective exec policy is authoritative: YOLO (`tools.exec.mode: "full"`) normally launches Claude with `--permission-mode bypassPermissions`, while a restrictive policy launches it with `--permission-mode default`. Root-run gateways also use `default` because Claude Code rejects bypass mode for root; OpenClaw still answers Claude's stdio tool-control requests from the configured exec policy. Per-agent `agents.entries.*.tools.exec` settings override the global `tools.exec` for that agent. Raw backend args may still include `--permission-mode`, but live Claude launches normalize that flag to match the effective policy and host restriction.
|
||||
|
||||
The backend also maps OpenClaw `/think` levels to Claude Code's native `--effort` flag: `minimal`/`low` -> `low`, `medium` -> `medium`, and `high`/`xhigh`/`max` pass through directly. This keeps the supported Fable 5 effort levels the same for subscription-backed Claude CLI and API-key routes. `adaptive` removes configured `--effort` flags and supplies no replacement, so Claude Code resolves effective effort from its own environment, settings, and model defaults. Other CLI backends need their owning plugin to declare an equivalent argv mapper before `/think` affects the spawned CLI.
|
||||
|
||||
@@ -164,7 +164,7 @@ Set `agents.defaults.cliBackends.claude-cli.command` only when the `claude` bina
|
||||
|
||||
## Sessions
|
||||
|
||||
- If the CLI supports sessions, set `sessionArg` (e.g. `--session-id`), or `sessionArgs` (placeholder `{sessionId}`) when the id needs to land in multiple flags.
|
||||
- If the CLI supports sessions, set `sessionArgs` with a `{sessionId}` placeholder (for example `["--session-id", "{sessionId}"]`).
|
||||
- If the CLI uses a resume subcommand with different flags, set `resumeArgs` (replaces `args` when resuming) and optionally `resumeOutput` for non-JSON resumes.
|
||||
- `sessionMode`:
|
||||
- `always`: always send a session id (new UUID if none stored).
|
||||
@@ -230,7 +230,7 @@ The bundled Anthropic plugin registers for `claude-cli`:
|
||||
| `output` | `jsonl` |
|
||||
| `input` | `stdin` |
|
||||
| `modelArg` | `--model` |
|
||||
| `sessionArg` | `--session-id` |
|
||||
| `sessionArgs` | `["--session-id", "{sessionId}"]` |
|
||||
| `sessionMode` | `always` |
|
||||
| `imageArg` | `@` |
|
||||
| `imagePathScope` | `workspace` |
|
||||
@@ -337,7 +337,7 @@ Claude CLI backends scale this cap with the resolved Claude context window inste
|
||||
| --------------------- | ----------------------------------------------------------------- |
|
||||
| CLI not found | Set `command` to a full path. |
|
||||
| Wrong model name | Use `modelAliases` to map `provider/model` to the CLI's model id. |
|
||||
| No session continuity | Ensure `sessionArg` is set and `sessionMode` is not `none`. |
|
||||
| No session continuity | Ensure `sessionArgs` is set and `sessionMode` is not `none`. |
|
||||
| Images ignored | Set `imageArg` and verify the CLI supports file paths. |
|
||||
|
||||
## Related
|
||||
|
||||
@@ -40,7 +40,7 @@ Optional repository root shown in the system prompt's Runtime line. If unset, Op
|
||||
### `agents.defaults.skills`
|
||||
|
||||
Optional default skill allowlist for agents that do not set
|
||||
`agents.list[].skills`.
|
||||
`agents.entries.*.skills`.
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -56,9 +56,9 @@ Optional default skill allowlist for agents that do not set
|
||||
```
|
||||
|
||||
- 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
|
||||
- Omit `agents.entries.*.skills` to inherit the defaults.
|
||||
- Set `agents.entries.*.skills: []` for no skills.
|
||||
- A non-empty `agents.entries.*.skills` list is the final set for that agent; it
|
||||
does not merge with defaults.
|
||||
|
||||
### `agents.defaults.skipBootstrap`
|
||||
@@ -98,7 +98,7 @@ Controls when workspace bootstrap files are injected into the system prompt. Def
|
||||
}
|
||||
```
|
||||
|
||||
Per-agent override: `agents.list[].contextInjection`. Omitted values inherit
|
||||
Per-agent override: `agents.entries.*.contextInjection`. Omitted values inherit
|
||||
`agents.defaults.contextInjection`.
|
||||
|
||||
### `agents.defaults.bootstrapMaxChars`
|
||||
@@ -111,7 +111,7 @@ Max characters per workspace bootstrap file before truncation. Default: `20000`.
|
||||
}
|
||||
```
|
||||
|
||||
Per-agent override: `agents.list[].bootstrapMaxChars`. Omitted values inherit
|
||||
Per-agent override: `agents.entries.*.bootstrapMaxChars`. Omitted values inherit
|
||||
`agents.defaults.bootstrapMaxChars`.
|
||||
|
||||
### `agents.defaults.bootstrapTotalMaxChars`
|
||||
@@ -124,7 +124,7 @@ Max total characters injected across all workspace bootstrap files. Default: `60
|
||||
}
|
||||
```
|
||||
|
||||
Per-agent override: `agents.list[].bootstrapTotalMaxChars`. Omitted values
|
||||
Per-agent override: `agents.entries.*.bootstrapTotalMaxChars`. Omitted values
|
||||
inherit `agents.defaults.bootstrapTotalMaxChars`.
|
||||
|
||||
### Per-agent bootstrap profile overrides
|
||||
@@ -188,11 +188,11 @@ knob.
|
||||
|
||||
Matching per-agent overrides:
|
||||
|
||||
- `agents.list[].skillsLimits.maxSkillsPromptChars`
|
||||
- `agents.list[].contextInjection`
|
||||
- `agents.list[].bootstrapMaxChars`
|
||||
- `agents.list[].bootstrapTotalMaxChars`
|
||||
- `agents.list[].contextLimits.*`
|
||||
- `agents.entries.*.skillsLimits.maxSkillsPromptChars`
|
||||
- `agents.entries.*.contextInjection`
|
||||
- `agents.entries.*.bootstrapMaxChars`
|
||||
- `agents.entries.*.bootstrapTotalMaxChars`
|
||||
- `agents.entries.*.contextLimits.*`
|
||||
|
||||
#### `agents.defaults.startupContext`
|
||||
|
||||
@@ -249,7 +249,7 @@ Shared defaults for bounded runtime context surfaces.
|
||||
- `postCompactionMaxChars`: AGENTS.md excerpt cap used during post-compaction
|
||||
refresh injection.
|
||||
|
||||
#### `agents.list[].contextLimits`
|
||||
#### `agents.entries.*.contextLimits`
|
||||
|
||||
Per-agent override for the shared `contextLimits` knobs. Omitted fields inherit
|
||||
from `agents.defaults.contextLimits`.
|
||||
@@ -284,7 +284,7 @@ does not affect reading `SKILL.md` files on demand.
|
||||
}
|
||||
```
|
||||
|
||||
#### `agents.list[].skillsLimits.maxSkillsPromptChars`
|
||||
#### `agents.entries.*.skillsLimits.maxSkillsPromptChars`
|
||||
|
||||
Per-agent override for the skills prompt budget.
|
||||
|
||||
@@ -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",
|
||||
@@ -401,22 +403,22 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
- `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.
|
||||
- `utilityModel`: optional `provider/model` ref or alias for short internal tasks. It currently powers generated Control UI session titles, Telegram DM topic titles, Discord auto-thread titles, and [progress-draft narration](/concepts/progress-drafts#narrated-status). When unset, OpenClaw derives the primary provider's declared small-model default when one exists (OpenAI → `gpt-5.6-luna`, Anthropic → `claude-haiku-4-5`); title tasks otherwise use the agent's primary model, and narration stays off. If a distinct utility model cannot prepare or complete a generated title, OpenClaw retries that title once with the primary model. For dashboard titles, automatic utility derivation and the regular fallback use the effective session provider and auth profile; an explicit utility model keeps its configured provider/auth. Set `utilityModel: ""` to skip the alternate utility route; dashboard title generation still proceeds directly to the regular session model. `agents.list[].utilityModel` overrides the default, and an operation-specific model override wins over both. Utility tasks make separate model calls and send task-specific content to the selected model provider. Dashboard title generation sends at most the first 1,000 characters of the first non-command message; narration sends the inbound request plus compact redacted tool summaries. Choose a provider that matches your cost and data-handling requirements.
|
||||
- `utilityModel`: optional `provider/model` ref or alias for short internal tasks. It currently powers generated Control UI session titles, Telegram DM topic titles, Discord auto-thread titles, and [progress-draft narration](/concepts/progress-drafts#narrated-status). When unset, OpenClaw derives the primary provider's declared small-model default when one exists (OpenAI → `gpt-5.6-luna`, Anthropic → `claude-haiku-4-5`); title tasks otherwise use the agent's primary model, and narration stays off. If a distinct utility model cannot prepare or complete a generated title, OpenClaw retries that title once with the primary model. For dashboard titles, automatic utility derivation and the regular fallback use the effective session provider and auth profile; an explicit utility model keeps its configured provider/auth. Set `utilityModel: ""` to skip the alternate utility route; dashboard title generation still proceeds directly to the regular session model. `agents.entries.*.utilityModel` overrides the default, and an operation-specific model override wins over both. Utility tasks make separate model calls and send task-specific content to the selected model provider. Dashboard title generation sends at most the first 1,000 characters of the first non-command message; narration sends the inbound request plus compact redacted tool summaries. Choose a provider that matches your cost and data-handling requirements.
|
||||
- `imageModel`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
|
||||
- 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,22 +427,22 @@ 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.list[].toolProgressDetail` overrides this default.
|
||||
- `reasoningDefault`: default reasoning visibility for agents. Values: `"off"`, `"on"`, `"stream"`. Per-agent `agents.list[].reasoningDefault` overrides this default. Configured reasoning defaults are only applied for owners, authorized senders, or operator-admin gateway contexts when no per-message or session reasoning override is set.
|
||||
- `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.
|
||||
- `reasoningDefault`: default reasoning visibility for agents. Values: `"off"`, `"on"`, `"stream"`. Per-agent `agents.entries.*.reasoningDefault` overrides this default. Configured reasoning defaults are only applied for owners, authorized senders, or operator-admin gateway contexts when no per-message or session reasoning override is set.
|
||||
- `elevatedDefault`: default elevated-output level for agents. Values: `"off"`, `"on"`, `"ask"`, `"full"`. Default: `"on"`.
|
||||
- `model.primary`: format `provider/model` (e.g. `openai/gpt-5.6-sol` for Codex OAuth access). 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`: configured aliases and per-model settings. Each entry can include `alias` (shortcut) and `params` (provider-specific, for example `temperature`, `maxTokens`, `cacheRetention`, `context1m`, `responsesServerCompaction`, `responsesCompactThreshold`, OpenRouter `provider` routing, `chat_template_kwargs`, `extra_body`/`extraBody`). Adding entries does not restrict model overrides.
|
||||
- Use `provider/*` entries such as `"openai/*": {}` or `"vllm/*": {}` to show all discovered models for selected providers without manually listing every model id.
|
||||
- Add `agentRuntime` to a `provider/*` entry when every dynamically discovered model for that provider should use the same runtime. Exact `provider/model` runtime policy still wins over the wildcard.
|
||||
- Safe metadata edits: use `openclaw config set agents.defaults.models '<json>' --strict-json --merge` to add entries. `config set` refuses replacements that would remove existing entries unless you pass `--replace`.
|
||||
- `modelPolicy.allow`: explicit override allowlist. Accepts aliases, exact `provider/model` refs, and trailing prefix wildcards such as `openai/*` or `clawrouter/anthropic/*`. Omit it or use `[]` to allow any model. `agents.list[].modelPolicy.allow` replaces the default policy for that agent; an explicit empty list opts that agent into allow-any.
|
||||
- `modelPolicy.allow`: explicit override allowlist. Accepts aliases, exact `provider/model` refs, and trailing prefix wildcards such as `openai/*` or `clawrouter/anthropic/*`. Omit it or use `[]` to allow any model. `agents.entries.*.modelPolicy.allow` replaces the default policy for that agent; an explicit empty list opts that agent into allow-any.
|
||||
- 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](/providers/openai#advanced-configuration).
|
||||
- `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](/reference/prompt-caching) for details.
|
||||
- `params` merge precedence (config): `agents.defaults.params` (global base) is overridden by `agents.defaults.models["provider/model"].params` (per-model), then `agents.entries.*.params` (matching agent id) overrides by key. See [Prompt Caching](/reference/prompt-caching) for details.
|
||||
- `models.providers.openrouter.params.provider`: OpenRouter-wide default provider-routing policy. OpenClaw forwards this to OpenRouter's request `provider` object; per-model `agents.defaults.models["openrouter/<model>"].params.provider` and agent params override by key. See [OpenRouter provider routing](/providers/openrouter#advanced-configuration).
|
||||
- `params.extra_body`/`params.extraBody`: advanced pass-through JSON merged into `api: "openai-completions"` request bodies for OpenAI-compatible proxies. If it collides with generated request keys, the extra body wins; non-native completions routes still strip OpenAI-only `store` afterward.
|
||||
- `params.chat_template_kwargs`: vLLM/OpenAI-compatible chat-template arguments merged into top-level `api: "openai-completions"` request bodies. For `vllm/nemotron-3-*` with thinking off, the bundled vLLM plugin automatically sends `enable_thinking: false` and `force_nonempty_content: true`; explicit `chat_template_kwargs` override generated defaults, and `extra_body.chat_template_kwargs` still has final precedence. Configured vLLM Qwen and Nemotron thinking models expose binary `/think` choices (`off`, `on`) instead of the multi-level effort ladder.
|
||||
@@ -448,7 +450,7 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
- `compat.supportedReasoningEfforts`: per-model OpenAI-compatible reasoning effort list. Include `"xhigh"` for custom endpoints that truly accept it; OpenClaw then exposes `/think xhigh` in command menus, Gateway session rows, session patch validation, agent CLI validation, and `llm-task` validation for that configured provider/model. Use `compat.reasoningEffortMap` when the backend wants a provider-specific value for a canonical level.
|
||||
- `params.preserveThinking`: Z.AI-only opt-in for preserved thinking. When enabled and thinking is on, OpenClaw sends `thinking.clear_thinking: false` and replays prior `reasoning_content`; see [Z.AI thinking and preserved thinking](/providers/zai#advanced-configuration).
|
||||
- `localService`: optional provider-level process manager for local/self-hosted model servers. When the selected model belongs to that provider, OpenClaw probes `healthUrl` (or `baseUrl + "/models"`), starts `command` with `args` if the endpoint is down, waits up to `readyTimeoutMs`, then sends the model request. `command` must be an absolute path. `idleStopMs: 0` keeps the process alive until OpenClaw exits; a positive value stops the OpenClaw-spawned process after that many idle milliseconds. See [Local model services](/gateway/local-model-services).
|
||||
- Runtime policy belongs on providers or models, not on `agents.defaults`. Use `models.providers.<provider>.agentRuntime` for provider-wide rules or `agents.defaults.models["provider/model"].agentRuntime` / `agents.list[].models["provider/model"].agentRuntime` for model-specific rules. A provider/model prefix alone never selects a harness. With runtime unset or `auto`, OpenAI may select Codex implicitly only for an exact official HTTPS Platform Responses or ChatGPT Responses route with no authored request override. See [OpenAI implicit agent runtime](/providers/openai#implicit-agent-runtime).
|
||||
- Runtime policy belongs on providers or models, not on `agents.defaults`. Use `models.providers.<provider>.agentRuntime` for provider-wide rules or `agents.defaults.models["provider/model"].agentRuntime` / `agents.entries.*.models["provider/model"].agentRuntime` for model-specific rules. A provider/model prefix alone never selects a harness. With runtime unset or `auto`, OpenAI may select Codex implicitly only for an exact official HTTPS Platform Responses or ChatGPT Responses route with no authored request override. See [OpenAI implicit agent runtime](/providers/openai#implicit-agent-runtime).
|
||||
- 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`.
|
||||
|
||||
@@ -482,8 +484,8 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
- `id`: `"auto"`, `"openclaw"`, a registered plugin harness id, or a supported CLI backend alias. The bundled Codex plugin registers `codex`; the bundled Anthropic plugin provides the `claude-cli` CLI backend.
|
||||
- `id: "auto"` lets registered plugin harnesses claim effective routes that declare or otherwise satisfy their support contract, and uses OpenClaw when no harness matches. An explicit plugin runtime such as `id: "codex"` requires that harness and a compatible effective route; it fails closed if either is unavailable or if execution fails.
|
||||
- `id: "pi"` is accepted only as a deprecated alias for `openclaw` to preserve shipped configs from v2026.5.22 and earlier. New config should use `openclaw`.
|
||||
- Runtime precedence is exact model policy first (`agents.list[].models["provider/model"]`, `agents.defaults.models["provider/model"]`, or `models.providers.<provider>.models[]`), then `agents.list[]` / `agents.defaults.models["provider/*"]`, then provider-wide policy at `models.providers.<provider>.agentRuntime`.
|
||||
- Whole-agent runtime keys are legacy. `agents.defaults.agentRuntime`, `agents.list[].agentRuntime`, session runtime pins, and `OPENCLAW_AGENT_RUNTIME` are ignored by runtime selection. Run `openclaw doctor --fix` to remove stale values.
|
||||
- Runtime precedence is exact model policy first (`agents.entries.*.models["provider/model"]`, `agents.defaults.models["provider/model"]`, or `models.providers.<provider>.models[]`), then `agents.entries.*` / `agents.defaults.models["provider/*"]`, then provider-wide policy at `models.providers.<provider>.agentRuntime`.
|
||||
- Whole-agent runtime keys are legacy. `agents.defaults.agentRuntime`, `agents.entries.*.agentRuntime`, session runtime pins, and `OPENCLAW_AGENT_RUNTIME` are ignored by runtime selection. Run `openclaw doctor --fix` to remove stale values.
|
||||
- Eligible exact official HTTPS OpenAI Responses/ChatGPT routes with no authored request override may use the Codex harness implicitly. Provider/model `agentRuntime.id: "codex"` makes Codex a fail-closed requirement but does not make an incompatible route compatible.
|
||||
- For Claude CLI deployments, prefer `model: "anthropic/claude-opus-4-8"` plus model-scoped `agentRuntime.id: "claude-cli"`. Legacy `claude-cli/<model>` refs still work for compatibility, but new config should keep provider/model selection canonical and put the execution backend in provider/model runtime policy.
|
||||
- This only controls text agent-turn execution. Media generation, vision, PDF, music, video, and TTS still use their provider/model settings.
|
||||
@@ -524,7 +526,7 @@ Optional CLI backends for text-only fallback runs (no tool calls). Useful as a b
|
||||
args: ["--json"],
|
||||
output: "json",
|
||||
modelArg: "--model",
|
||||
sessionArg: "--session",
|
||||
sessionArgs: ["--session", "{sessionId}"],
|
||||
sessionMode: "existing",
|
||||
systemPromptArg: "--system",
|
||||
// Or use systemPromptFileArg when the CLI accepts a prompt file flag.
|
||||
@@ -539,7 +541,7 @@ Optional CLI backends for text-only fallback runs (no tool calls). Useful as a b
|
||||
```
|
||||
|
||||
- CLI backends are text-first; tools are always disabled.
|
||||
- Sessions supported when `sessionArg` is set.
|
||||
- Sessions are supported when `sessionArgs` includes `{sessionId}`.
|
||||
- Image pass-through supported when `imageArg` accepts file paths.
|
||||
- `reseedFromRawTranscriptWhenUncompacted: true` lets a backend recover safe
|
||||
invalidated sessions from a bounded raw OpenClaw transcript tail before the
|
||||
@@ -606,7 +608,7 @@ Periodic heartbeat runs.
|
||||
- `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.
|
||||
- `skipWhenBusy`: when true, heartbeat runs defer on that agent's extra busy lanes: its own session-keyed subagent or nested command work. Cron lanes always defer heartbeats, even without this flag.
|
||||
- Per-agent: set `agents.list[].heartbeat`. When any agent defines `heartbeat`, **only those agents** run heartbeats.
|
||||
- Per-agent: set `agents.entries.*.heartbeat`. When any agent defines `heartbeat`, **only those agents** run heartbeats.
|
||||
- Heartbeats run full agent turns — shorter intervals burn more tokens.
|
||||
|
||||
### `agents.defaults.compaction`
|
||||
@@ -622,12 +624,11 @@ Periodic heartbeat runs.
|
||||
timeoutSeconds: 180,
|
||||
keepRecentTokens: 50000,
|
||||
recentTurnsPreserve: 3,
|
||||
identifierPolicy: "strict", // strict | off | custom
|
||||
identifierInstructions: "Preserve deployment IDs, ticket IDs, and host:port pairs exactly.", // used when identifierPolicy=custom
|
||||
identifierPolicy: "strict", // strict | off
|
||||
qualityGuard: { enabled: true, maxRetries: 1 },
|
||||
midTurnPrecheck: { enabled: false }, // optional tool-loop pressure check
|
||||
postIndexSync: "async", // off | async | await
|
||||
postCompactionSections: ["Session Startup", "Red Lines"], // opt in to AGENTS.md section reinjection
|
||||
postCompactionSections: ["Session Startup", "Red Lines"],
|
||||
model: "openrouter/anthropic/claude-sonnet-4-6", // optional compaction-only model override
|
||||
truncateAfterCompaction: true, // rotate to a smaller successor JSONL after compaction
|
||||
maxActiveTranscriptBytes: "20mb", // optional preflight local compaction trigger
|
||||
@@ -637,8 +638,6 @@ Periodic heartbeat runs.
|
||||
model: "ollama/qwen3:8b", // optional memory-flush-only model override
|
||||
softThresholdTokens: 6000,
|
||||
forceFlushTranscriptBytes: "2mb",
|
||||
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.",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -652,18 +651,23 @@ Periodic heartbeat runs.
|
||||
- `timeoutSeconds`: maximum seconds allowed for a single compaction operation before OpenClaw aborts it. Default: `180`.
|
||||
- `keepRecentTokens`: agent cut-point budget for keeping the most recent transcript tail verbatim. Manual `/compact` honors this when explicitly set; otherwise manual compaction is a hard checkpoint.
|
||||
- `recentTurnsPreserve`: number of most recent user/assistant turns kept verbatim outside safeguard summarization. Default: `3`.
|
||||
- `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`.
|
||||
- `identifierPolicy`: `strict` (default) or `off`. `strict` prepends built-in opaque identifier retention guidance during compaction summarization.
|
||||
- `qualityGuard`: retry-on-malformed-output checks for safeguard summaries. Enabled by default in safeguard mode; set `enabled: false` to skip the audit.
|
||||
- `midTurnPrecheck`: optional tool-loop pressure check. When `enabled: true`, OpenClaw checks context pressure after tool results are appended and before the next model call. If the context no longer fits, it aborts the current attempt before submitting the prompt and reuses the existing precheck recovery path to truncate tool results or compact and retry. Works with both `default` and `safeguard` compaction modes. Default: disabled.
|
||||
- `postIndexSync`: post-compaction session-memory reindex mode. Default: `"async"`. Use `"await"` for strongest freshness, `"async"` for lower compaction latency, or `"off"` only when session-memory sync is handled elsewhere.
|
||||
- `postCompactionSections`: optional AGENTS.md H2/H3 section names to re-inject after compaction. Reinjection is disabled when unset or set to `[]`. Explicitly setting `["Session Startup", "Red Lines"]` enables that pair and preserves the legacy `Every Session`/`Safety` fallback. Enable this only when the extra context is worth the risk of duplicating project guidance already captured in the compaction summary.
|
||||
- `postCompactionSections`: optional AGENTS.md H2/H3 section names to re-inject after compaction. Leave unset or use `[]` to disable.
|
||||
- `model`: optional `provider/model-id` or bare alias from `agents.defaults.models` for compaction summarization only. Bare aliases resolve before dispatch; configured literal model IDs retain precedence on collisions. 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.
|
||||
- `truncateAfterCompaction`: rotates the active session transcript after compaction so future turns load only the summary and unsummarized tail, while the previous full transcript remains archived. Prevents unbounded active transcript growth in long-running sessions. Default: `false`.
|
||||
- `maxActiveTranscriptBytes`: optional byte threshold (`number` or strings like `"20mb"`) that triggers normal local compaction before a run when transcript history grows past the threshold. Requires `truncateAfterCompaction` so successful compaction can rotate to a smaller successor transcript. Disabled when unset or `0`.
|
||||
- `notifyUser`: when `true`, sends brief context-maintenance notices to the user: when compaction starts and completes (for example, "Compacting context..." and "Compaction complete"), and when a pre-compaction memory flush is exhausted so the reply continues in a degraded state (for example, "Memory maintenance temporarily failed; continuing your reply."). Disabled by default to keep these notices silent.
|
||||
- `memoryFlush`: silent agentic turn before auto-compaction to store durable memories. Set `model` to an exact provider/model such as `ollama/qwen3:8b` when this housekeeping turn should stay on a local model; the override does not inherit the active session fallback chain. `forceFlushTranscriptBytes` forces the flush when transcript size reaches the threshold even if token counters are stale. Skipped when workspace is read-only.
|
||||
|
||||
Custom compaction instructions are code-owned. Implement a compaction provider
|
||||
plugin with `summarize()` for custom summary construction, and use
|
||||
`before_prompt_build` when post-compaction context must be injected into later
|
||||
model prompts. Doctor strips the retired instruction fields and points to these
|
||||
seams.
|
||||
|
||||
### `agents.defaults.contextPruning`
|
||||
|
||||
Prunes **old tool results** from in-memory context before sending to the LLM. Does **not** modify session history on disk. Disabled by default; set `mode: "cache-ttl"` to enable.
|
||||
@@ -718,7 +722,7 @@ See [Session Pruning](/concepts/session-pruning) for behavior details.
|
||||
- Non-Telegram channels require explicit `*.streaming.block.enabled: true` to enable block replies. QQ Bot is the exception: it has no `streaming.block` keys and streams block replies unless `channels.qqbot.streaming.mode` is `"off"`.
|
||||
- Channel overrides: `channels.<channel>.streaming.block.coalesce` (and per-account variants). Discord, Google Chat, Mattermost, MS Teams, Signal, and Slack default `minChars: 1500` / `idleMs: 1000`.
|
||||
- `blockStreamingChunk.breakPreference`: preferred chunk boundary (`"paragraph" | "newline" | "sentence"`).
|
||||
- `humanDelay`: randomized pause between block replies. Default: `off`. `natural` = 800-2500ms. `custom` uses `minMs`/`maxMs` (falls back to the natural range for any unset bound). Per-agent override: `agents.list[].humanDelay`.
|
||||
- `humanDelay`: randomized pause between block replies. Default: `off`. `natural` = 800-2500ms. `custom` uses `minMs`/`maxMs` (falls back to the natural range for any unset bound). Per-agent override: `agents.entries.*.humanDelay`.
|
||||
|
||||
See [Streaming](/concepts/streaming) for behavior + chunking details.
|
||||
|
||||
@@ -737,7 +741,7 @@ See [Streaming](/concepts/streaming) for behavior + chunking details.
|
||||
|
||||
- Defaults: `instant` for direct chats/mentions, `message` for unmentioned group chats.
|
||||
- `typingIntervalSeconds` default: `6`.
|
||||
- Per-session overrides: `session.typingMode`.
|
||||
- Per-agent overrides: `agents.entries.*.typingMode` and `agents.entries.*.typingIntervalSeconds`.
|
||||
|
||||
See [Typing Indicators](/concepts/typing-indicators).
|
||||
|
||||
@@ -983,11 +987,11 @@ scripts/sandbox-browser-setup.sh # optional browser image
|
||||
|
||||
For npm installs without a source checkout, see [Sandboxing § Images and setup](/gateway/sandboxing#images-and-setup) for inline `docker build` commands.
|
||||
|
||||
### `agents.list` (per-agent overrides)
|
||||
### `agents.entries` (per-agent overrides)
|
||||
|
||||
Use `agents.list[].tts` to give an agent its own TTS provider, voice, model,
|
||||
Use `agents.entries.*.tts` to give an agent its own TTS provider, voice, model,
|
||||
style, or auto-TTS mode. The agent block deep-merges over global
|
||||
`messages.tts`, so shared credentials can stay in one place while individual
|
||||
`tts`, so shared credentials can stay in one place while individual
|
||||
agents override only the voice or provider fields they need. The active agent's
|
||||
override applies to automatic spoken replies, `/tts audio`, `/tts status`, and
|
||||
the `tts` agent tool. See [Text-to-speech](/tools/tts#per-agent-voice-overrides)
|
||||
@@ -1050,7 +1054,7 @@ for provider examples and precedence.
|
||||
- `model`: string form sets a strict per-agent primary with no model fallback; object form `{ primary }` is also strict unless you add `fallbacks`. Use `{ primary, fallbacks: [...] }` to opt that agent into fallback, or `{ primary, fallbacks: [] }` to make strict behavior explicit. Cron jobs that only override `primary` still inherit default fallbacks unless you set `fallbacks: []`.
|
||||
- `utilityModel`: optional per-agent override for short internal tasks such as generated session and thread titles. Falls back to `agents.defaults.utilityModel`, then the effective session provider's declared small-model default. Dashboard titles retry once with the effective regular session model. An empty string skips the alternate utility route for this agent without disabling dashboard title generation.
|
||||
- `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.
|
||||
- `tts`: optional per-agent text-to-speech overrides. The block deep-merges over `messages.tts`, so keep shared provider credentials and fallback policy in `messages.tts` and set only persona-specific values such as provider, voice, model, style, or auto mode here.
|
||||
- `tts`: optional per-agent text-to-speech overrides. The block deep-merges over `tts`, so keep shared provider credentials and fallback policy in `tts` and set only persona-specific values such as provider, voice, model, style, or auto mode here.
|
||||
- `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. The selected provider/model profile controls which values are valid; for Google Gemini, `adaptive` keeps provider-owned dynamic thinking (`thinkingLevel` omitted on Gemini 3/3.1, `thinkingBudget: -1` on Gemini 2.5).
|
||||
- `reasoningDefault`: optional per-agent default reasoning visibility (`on | off | stream`). Overrides `agents.defaults.reasoningDefault` for this agent when no per-message or session reasoning override is set.
|
||||
@@ -1060,7 +1064,7 @@ for provider examples and precedence.
|
||||
- `identity.avatar`: workspace-relative path, `http(s)` URL, or `data:` URI.
|
||||
- Local workspace-relative `identity.avatar` image files are limited to 2 MB. `http(s)` URLs and `data:` URIs are not checked against the local file-size limit.
|
||||
- `identity` derives defaults: `ackReaction` from `emoji`, `mentionPatterns` from `name`/`emoji`.
|
||||
- `subagents.allowAgents`: allowlist of configured agent ids for explicit `sessions_spawn.agentId` targets (`["*"]` = any configured target; default: same agent only). Include the requester id when self-targeted `agentId` calls should be allowed. Stale entries whose agent config was deleted are rejected by `sessions_spawn` and omitted from `agents_list`; run `openclaw doctor --fix` to clean them up, or add a minimal `agents.list[]` entry if that target should remain spawnable while inheriting defaults.
|
||||
- `subagents.allowAgents`: allowlist of configured agent ids for explicit `sessions_spawn.agentId` targets (`["*"]` = any configured target; default: same agent only). Include the requester id when self-targeted `agentId` calls should be allowed. Stale entries whose agent config was deleted are rejected by `sessions_spawn` and omitted from `agents_list`; run `openclaw doctor --fix` to clean them up, or add a minimal `agents.entries.*` entry if that target should remain spawnable while inheriting defaults.
|
||||
- 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).
|
||||
- `subagents.maxConcurrent`: max concurrent child-agent runs across subagent execution. Default: `8`.
|
||||
@@ -1269,7 +1273,7 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
|
||||
- `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. Dock commands such as `/dock_discord` use the same map to switch the active session's reply route to another linked channel peer; see [Channel docking](/concepts/channel-docking).
|
||||
- **`reset`**: primary reset policy. `none` disables automatic reset and is the default; compaction bounds active context instead. `daily` resets at `atHour` local time; `idle` resets after `idleMinutes`. When both configured, whichever expires first wins. `/new` and `/reset` remain available in every mode. Daily reset freshness uses the session row's `sessionStartedAt`; idle reset freshness uses `lastInteractionAt`. Background/system-event writes such as heartbeat, cron wakeups, exec notifications, and gateway bookkeeping can update `updatedAt`, but they do not keep daily/idle sessions fresh.
|
||||
- **`resetByType`**: per-type overrides (`direct`, `group`, `thread`). Legacy `dm` accepted as alias for `direct`.
|
||||
- **`resetByType`**: per-type overrides (`direct`, `group`, `thread`). Doctor migrates legacy `dm` entries to `direct`; the schema rejects `dm`.
|
||||
- **`resetByChannel`**: per-channel reset overrides keyed by provider/channel id. When the session's channel has a matching entry, it wins outright over `resetByType`/`reset` for that session. Use only when one channel needs reset behavior different from the type-level policy.
|
||||
- **`mainKey`**: legacy field. Runtime always uses `"main"` for the main direct-chat bucket.
|
||||
- **`sendPolicy`**: match by `channel`, `chatType` (`direct|group|channel`, with legacy `dm` alias), `keyPrefix`, or `rawKeyPrefix`. First deny wins.
|
||||
@@ -1283,7 +1287,7 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
|
||||
- `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`)
|
||||
- `enabled`: master switch for supported channel thread bindings
|
||||
- `idleHours`: default inactivity auto-unfocus in hours (`0` disables; providers can override)
|
||||
- `maxAgeHours`: default hard max age in hours (`0` disables; providers can override)
|
||||
- `spawnSessions`: default gate for creating thread-bound work sessions from `sessions_spawn` and ACP thread spawns. Defaults to `true` when thread bindings are enabled; providers/accounts can override.
|
||||
@@ -1377,7 +1381,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`: outbound WhatsApp reply prefix. Doctor moves the retired inbound `messagePrefix` value here only when this canonical value is unset.
|
||||
- `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.
|
||||
@@ -1387,50 +1391,52 @@ Batches rapid text-only messages from the same sender into a single agent turn.
|
||||
|
||||
```json5
|
||||
{
|
||||
messages: {
|
||||
tts: {
|
||||
auto: "off", // off (default) | always | inbound | tagged
|
||||
mode: "final", // final | all
|
||||
provider: "elevenlabs",
|
||||
summaryModel: "openai/gpt-5.4-mini",
|
||||
modelOverrides: { enabled: true },
|
||||
maxTextLength: 4000,
|
||||
timeoutMs: 30000,
|
||||
prefsPath: "~/.openclaw/settings/tts.json",
|
||||
providers: {
|
||||
elevenlabs: {
|
||||
apiKey: "elevenlabs_api_key",
|
||||
baseUrl: "https://api.elevenlabs.io",
|
||||
speakerVoiceId: "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,
|
||||
},
|
||||
},
|
||||
microsoft: {
|
||||
speakerVoice: "en-US-MichelleNeural",
|
||||
lang: "en-US",
|
||||
outputFormat: "audio-24khz-48kbitrate-mono-mp3",
|
||||
},
|
||||
openai: {
|
||||
apiKey: "openai_api_key",
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
model: "gpt-4o-mini-tts",
|
||||
speakerVoice: "coral",
|
||||
tts: {
|
||||
auto: "off", // off (default) | always | inbound | tagged
|
||||
mode: "final", // final | all
|
||||
provider: "elevenlabs",
|
||||
summaryModel: "openai/gpt-5.4-mini",
|
||||
modelOverrides: { enabled: true },
|
||||
maxTextLength: 4000,
|
||||
timeoutMs: 30000,
|
||||
providers: {
|
||||
elevenlabs: {
|
||||
apiKey: "example-elevenlabs-api-key",
|
||||
baseUrl: "https://api.elevenlabs.io",
|
||||
speakerVoiceId: "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,
|
||||
},
|
||||
},
|
||||
microsoft: {
|
||||
speakerVoice: "en-US-MichelleNeural",
|
||||
lang: "en-US",
|
||||
outputFormat: "audio-24khz-48kbitrate-mono-mp3",
|
||||
},
|
||||
openai: {
|
||||
apiKey: "example-openai-api-key",
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
model: "gpt-4o-mini-tts",
|
||||
speakerVoice: "coral",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The global preferences path is machine state (default
|
||||
`~/.openclaw/settings/tts.json`; override with `OPENCLAW_TTS_PREFS`). Advanced
|
||||
multi-agent setups can set `agents.entries.<id>.tts.prefsPath` for distinct
|
||||
per-agent preference stores.
|
||||
|
||||
- `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 (`enabled !== false`); `modelOverrides.allowProvider` is opt-in.
|
||||
|
||||
@@ -394,11 +394,8 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
audience: "https://gateway.example.com/googlechat",
|
||||
webhookPath: "/googlechat",
|
||||
botUser: "users/1234567890",
|
||||
dm: {
|
||||
enabled: true,
|
||||
policy: "pairing",
|
||||
allowFrom: ["users/1234567890"],
|
||||
},
|
||||
dmPolicy: "pairing",
|
||||
allowFrom: ["users/1234567890"],
|
||||
groupPolicy: "allowlist",
|
||||
groups: {
|
||||
"spaces/AAAA": { allow: true, requireMention: true },
|
||||
@@ -412,7 +409,7 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
```
|
||||
|
||||
- Service account JSON: inline (`serviceAccount`) or file-based (`serviceAccountFile`).
|
||||
- Service account SecretRef is also supported (`serviceAccountRef`).
|
||||
- `serviceAccount` accepts a SecretRef directly.
|
||||
- Env fallbacks: `GOOGLE_CHAT_SERVICE_ACCOUNT` or `GOOGLE_CHAT_SERVICE_ACCOUNT_FILE` (default account only).
|
||||
- Use `spaces/<spaceId>` or `users/<userId>` for delivery targets.
|
||||
- `channels.googlechat.dangerouslyAllowNameMatching` re-enables mutable email principal matching (break-glass compatibility mode).
|
||||
@@ -839,7 +836,7 @@ Fix: either pick a stronger tool-calling model, remove the explicit `"message_to
|
||||
**Mention types:**
|
||||
|
||||
- **Metadata mentions**: Native platform @-mentions. Ignored in WhatsApp self-chat mode.
|
||||
- **Text patterns**: Safe regex patterns in `agents.list[].groupChat.mentionPatterns`. Invalid patterns and unsafe nested repetition are ignored.
|
||||
- **Text patterns**: Safe regex patterns in `agents.entries.*.groupChat.mentionPatterns`. Invalid patterns and unsafe nested repetition are ignored.
|
||||
- Mention gating is enforced only when detection is possible (native mentions or at least one pattern).
|
||||
|
||||
```json5
|
||||
|
||||
@@ -133,7 +133,7 @@ Global tool allow/deny policy (deny wins). Case-insensitive, supports `*` wildca
|
||||
```
|
||||
|
||||
<Note>
|
||||
`allow` and `alsoAllow` cannot both be set in the same scope (`tools`, `tools.byProvider.<id>`, `agents.list[].tools`) — config validation rejects it. Merge `alsoAllow` entries into `allow`, or drop `allow` and use `profile` + `alsoAllow` instead.
|
||||
`allow` and `alsoAllow` cannot both be set in the same scope (`tools`, `tools.byProvider.<id>`, `agents.entries.*.tools`) — config validation rejects it. Merge `alsoAllow` entries into `allow`, or drop `allow` and use `profile` + `alsoAllow` instead.
|
||||
</Note>
|
||||
|
||||
### `tools.byProvider`
|
||||
@@ -170,7 +170,7 @@ Restricts tools for a specific requester identity. This is defense-in-depth on t
|
||||
|
||||
Keys use explicit prefixes: `channel:<channelId>:<senderId>`, `id:<senderId>`, `e164:<phone>`, `username:<handle>`, `name:<displayName>`, or `"*"`. Channel ids are canonical OpenClaw ids; aliases such as `teams` normalize to `msteams`. Legacy unprefixed keys are accepted as `id:` only. Matching order is channel+id, id, e164, username, name, then wildcard.
|
||||
|
||||
Per-agent `agents.list[].tools.toolsBySender` overrides the global sender match when it matches, even with an empty `{}` policy.
|
||||
Per-agent `agents.entries.*.tools.toolsBySender` overrides the global sender match when it matches, even with an empty `{}` policy.
|
||||
|
||||
### `tools.elevated`
|
||||
|
||||
@@ -190,7 +190,7 @@ Controls elevated exec access outside the sandbox:
|
||||
}
|
||||
```
|
||||
|
||||
- Per-agent override (`agents.list[].tools.elevated`) can only further restrict.
|
||||
- Per-agent override (`agents.entries.*.tools.elevated`) can only further restrict.
|
||||
- `/elevated on|off|ask|full` stores state per session; inline directives apply to single message.
|
||||
- Elevated `exec` bypasses sandboxing and uses the configured escape path (`gateway` by default, or `node` when the exec target is `node`).
|
||||
|
||||
@@ -220,7 +220,7 @@ Values shown are defaults except `applyPatch.allowModels` (empty/unset by defaul
|
||||
|
||||
### `tools.loopDetection`
|
||||
|
||||
Tool-loop safety checks are **disabled by default**. Set `enabled: true` to activate detection. Settings can be defined globally in `tools.loopDetection` and overridden per-agent at `agents.list[].tools.loopDetection`.
|
||||
Tool-loop safety checks are **disabled by default**. Set `enabled: true` to activate detection. Settings can be defined globally in `tools.loopDetection` and overridden per-agent at `agents.entries.*.tools.loopDetection`.
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -273,34 +273,26 @@ Configures inbound media understanding (image/audio/video):
|
||||
tools: {
|
||||
media: {
|
||||
concurrency: 2,
|
||||
audio: {
|
||||
enabled: true,
|
||||
maxBytes: 20971520,
|
||||
scope: {
|
||||
default: "deny",
|
||||
rules: [{ action: "allow", match: { chatType: "direct" } }],
|
||||
models: [
|
||||
{ provider: "openai", model: "gpt-4o-mini-transcribe", capabilities: ["audio"] },
|
||||
{
|
||||
type: "cli",
|
||||
command: "whisper",
|
||||
args: ["--model", "base", "{{MediaPath}}"],
|
||||
capabilities: ["audio"],
|
||||
},
|
||||
models: [
|
||||
{ provider: "openai", model: "gpt-4o-mini-transcribe" },
|
||||
{ type: "cli", command: "whisper", args: ["--model", "base", "{{MediaPath}}"] },
|
||||
],
|
||||
},
|
||||
image: {
|
||||
enabled: true,
|
||||
timeoutSeconds: 180,
|
||||
models: [{ provider: "ollama", model: "gemma4:26b", timeoutSeconds: 300 }],
|
||||
},
|
||||
video: {
|
||||
enabled: true,
|
||||
maxBytes: 52428800,
|
||||
models: [{ provider: "google", model: "gemini-3-flash-preview" }],
|
||||
},
|
||||
{ provider: "ollama", model: "gemma4:26b", capabilities: ["image"] },
|
||||
{ provider: "google", model: "gemini-3-flash-preview", capabilities: ["video"] },
|
||||
],
|
||||
audio: { enabled: true, preferredModel: "openai/gpt-4o-mini-transcribe" },
|
||||
image: { enabled: true, preferredModel: "ollama/gemma4:26b" },
|
||||
video: { enabled: true },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`concurrency` (default `2`), `audio.maxBytes` (default 20 MB), and `video.maxBytes` (default 50 MB) are shown at their defaults; `image.maxBytes` defaults to 10 MB. Per-capability request timeout defaults: image/audio `60`s, video `120`s.
|
||||
`tools.media.models` is the only configured model list. Every entry declares the capabilities it handles. The optional `preferredModel` selector accepts `provider/model`, a model id, `provider:<id>` for provider-default entries, or `cli:command`; matching entries move to the front of that capability's fallback order. Per-capability prompts, limits, request settings, scope, attachment policy, and audio transcript echo remain defaults for configured and auto-detected models; a model entry can override model-specific fields.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Media model entry fields">
|
||||
@@ -317,9 +309,9 @@ Configures inbound media understanding (image/audio/video):
|
||||
|
||||
**Common fields:**
|
||||
|
||||
- `capabilities`: optional list (`image`, `audio`, `video`). Each provider plugin declares its own default capability set; for example the bundled `openai` provider defaults to image+audio, `anthropic`/`minimax` to image, `google` to image+audio+video, and `groq` to audio.
|
||||
- `capabilities`: list containing one or more of `image`, `audio`, and `video`.
|
||||
- `prompt`, `maxChars`, `maxBytes`, `timeoutSeconds`, `language`: per-entry overrides.
|
||||
- `tools.media.image.timeoutSeconds` and matching image model `timeoutSeconds` entries also apply when the agent calls the explicit `image` tool. For image understanding, this timeout applies to the request itself and is not reduced by earlier preparation work.
|
||||
- Matching image model `timeoutSeconds` entries also apply when the agent calls the explicit `image` tool. For image understanding, this timeout applies to the request itself and is not reduced by earlier preparation work.
|
||||
- Failures fall back to the next entry.
|
||||
|
||||
Provider auth follows standard order: `auth-profiles.json` → env vars → `models.providers.*.apiKey`.
|
||||
|
||||
@@ -31,16 +31,15 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
workspace: "~/.openclaw/workspace",
|
||||
model: { primary: "anthropic/claude-sonnet-4-6" },
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
entries: {
|
||||
main: {
|
||||
identity: {
|
||||
name: "Clawd",
|
||||
theme: "helpful assistant",
|
||||
emoji: "🦞",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
whatsapp: {
|
||||
@@ -90,7 +89,7 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
},
|
||||
},
|
||||
|
||||
// Identity is per agent — set it on agents.list[].identity below.
|
||||
// Identity is per agent — set it on agents.entries.<id>.identity below.
|
||||
|
||||
// Logging
|
||||
logging: {
|
||||
@@ -103,7 +102,6 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
|
||||
// Message formatting
|
||||
messages: {
|
||||
messagePrefix: "[openclaw]",
|
||||
visibleReplies: "automatic",
|
||||
responsePrefix: ">",
|
||||
ackReaction: "👀",
|
||||
@@ -115,7 +113,6 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
},
|
||||
queue: {
|
||||
mode: "followup",
|
||||
debounceMs: 500,
|
||||
cap: 20,
|
||||
drop: "summarize",
|
||||
byChannel: {
|
||||
@@ -130,27 +127,6 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
},
|
||||
},
|
||||
|
||||
// Tooling
|
||||
tools: {
|
||||
media: {
|
||||
audio: {
|
||||
enabled: true,
|
||||
maxBytes: 20971520,
|
||||
models: [
|
||||
{ provider: "openai", model: "gpt-4o-transcribe" },
|
||||
// Optional CLI fallback (Whisper binary):
|
||||
// { type: "cli", command: "whisper", args: ["--model", "base", "{{MediaPath}}"] }
|
||||
],
|
||||
timeoutSeconds: 120,
|
||||
},
|
||||
video: {
|
||||
enabled: true,
|
||||
maxBytes: 52428800,
|
||||
models: [{ provider: "google", model: "gemini-3-flash-preview" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Session behavior
|
||||
session: {
|
||||
scope: "per-sender",
|
||||
@@ -201,7 +177,8 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
discord: {
|
||||
enabled: true,
|
||||
token: "YOUR_DISCORD_BOT_TOKEN",
|
||||
dm: { enabled: true, allowFrom: ["123456789012345678"] },
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: ["123456789012345678"],
|
||||
guilds: {
|
||||
"123456789012345678": {
|
||||
slug: "friends-of-openclaw",
|
||||
@@ -221,7 +198,8 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
channels: {
|
||||
"#general": { enabled: true, requireMention: true },
|
||||
},
|
||||
dm: { enabled: true, allowFrom: ["U123"] },
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: ["U123"],
|
||||
slashCommand: {
|
||||
enabled: true,
|
||||
name: "openclaw",
|
||||
@@ -280,14 +258,6 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
prompt: "HEARTBEAT",
|
||||
ackMaxChars: 300,
|
||||
},
|
||||
memorySearch: {
|
||||
provider: "gemini",
|
||||
model: "gemini-embedding-001",
|
||||
remote: {
|
||||
apiKey: "${GEMINI_API_KEY}",
|
||||
},
|
||||
extraPaths: ["../team-docs", "/srv/shared-notes"],
|
||||
},
|
||||
sandbox: {
|
||||
mode: "non-main",
|
||||
scope: "session", // preferred over legacy perSession: true
|
||||
@@ -305,9 +275,8 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
},
|
||||
},
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
entries: {
|
||||
main: {
|
||||
default: true,
|
||||
identity: {
|
||||
name: "Samantha",
|
||||
@@ -322,21 +291,39 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
reasoningDefault: "on", // per-agent reasoning visibility
|
||||
fastModeDefault: false, // per-agent fast mode
|
||||
},
|
||||
{
|
||||
id: "quick",
|
||||
quick: {
|
||||
skills: [], // no skills for this agent
|
||||
fastModeDefault: true, // this agent always runs fast
|
||||
thinkingDefault: "off",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
memory: {
|
||||
search: {
|
||||
provider: "gemini",
|
||||
model: "gemini-embedding-001",
|
||||
remote: {
|
||||
apiKey: "${GEMINI_API_KEY}",
|
||||
},
|
||||
extraPaths: ["../team-docs", "/srv/shared-notes"],
|
||||
},
|
||||
},
|
||||
|
||||
tools: {
|
||||
media: {
|
||||
models: [
|
||||
{ provider: "openai", model: "gpt-4o-transcribe", capabilities: ["audio"] },
|
||||
{ provider: "google", model: "gemini-3-flash-preview", capabilities: ["video"] },
|
||||
],
|
||||
audio: { enabled: true, maxBytes: 20971520, timeoutSeconds: 120 },
|
||||
video: { enabled: true, maxBytes: 52428800 },
|
||||
},
|
||||
allow: ["exec", "process", "read", "write", "edit", "apply_patch"],
|
||||
deny: ["browser", "canvas"],
|
||||
exec: {
|
||||
backgroundMs: 10000,
|
||||
timeoutSec: 1800,
|
||||
timeoutSeconds: 1800,
|
||||
cleanupMs: 1800000,
|
||||
},
|
||||
elevated: {
|
||||
@@ -442,7 +429,7 @@ Save to `~/.openclaw/openclaw.json` and you can DM the bot from that number.
|
||||
},
|
||||
tailscale: { mode: "serve", resetOnExit: false },
|
||||
remote: { url: "ws://gateway-host.ts.net:18789", token: "remote-token" },
|
||||
reload: { mode: "hybrid", debounceMs: 300 },
|
||||
reload: { mode: "hybrid" },
|
||||
},
|
||||
|
||||
skills: {
|
||||
@@ -501,16 +488,16 @@ example `~/.agents/skills/manager -> ~/Projects/manager/skills`.
|
||||
workspace: "~/.openclaw/workspace",
|
||||
skills: ["github", "weather"],
|
||||
},
|
||||
list: [
|
||||
{ id: "main", default: true },
|
||||
{ id: "docs", workspace: "~/.openclaw/workspace-docs", skills: ["docs-search"] },
|
||||
],
|
||||
entries: {
|
||||
main: { default: true },
|
||||
docs: { workspace: "~/.openclaw/workspace-docs", skills: ["docs-search"] },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `agents.defaults.skills` is the shared baseline.
|
||||
- `agents.list[].skills` replaces that baseline for one agent.
|
||||
- `agents.entries.*.skills` replaces that baseline for one agent.
|
||||
- Use `skills: []` when an agent should see no skills.
|
||||
|
||||
### Multi-platform setup
|
||||
@@ -519,7 +506,7 @@ example `~/.agents/skills/manager -> ~/Projects/manager/skills`.
|
||||
{
|
||||
agents: { defaults: { workspace: "~/.openclaw/workspace" } },
|
||||
channels: {
|
||||
whatsapp: { allowFrom: ["+15555550123"] },
|
||||
whatsapp: { allowFrom: ["+15555550123"], responsePrefix: "[openclaw]" },
|
||||
telegram: {
|
||||
enabled: true,
|
||||
botToken: "YOUR_TOKEN",
|
||||
@@ -528,7 +515,7 @@ example `~/.agents/skills/manager -> ~/Projects/manager/skills`.
|
||||
discord: {
|
||||
enabled: true,
|
||||
token: "YOUR_TOKEN",
|
||||
dm: { allowFrom: ["123456789012345678"] },
|
||||
allowFrom: ["123456789012345678"],
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -576,7 +563,7 @@ If more than one person can DM your bot (multiple entries in `allowFrom`, pairin
|
||||
discord: {
|
||||
enabled: true,
|
||||
token: "YOUR_DISCORD_BOT_TOKEN",
|
||||
dm: { enabled: true, allowFrom: ["123456789012345678", "987654321098765432"] },
|
||||
allowFrom: ["123456789012345678", "987654321098765432"],
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -630,15 +617,14 @@ Only enable direct mutable name/email/nick matching with each channel's `dangero
|
||||
workspace: "~/work-openclaw",
|
||||
elevatedDefault: "off",
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
entries: {
|
||||
main: {
|
||||
identity: {
|
||||
name: "WorkBot",
|
||||
theme: "professional assistant",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
channels: {
|
||||
slack: {
|
||||
|
||||
@@ -18,7 +18,7 @@ Code truth beats this page:
|
||||
|
||||
Dedicated deep references:
|
||||
|
||||
- [Memory configuration reference](/reference/memory-config) for `agents.defaults.memorySearch.*`, `memory.qmd.*`, `memory.citations`, and dreaming config under `plugins.entries.memory-core.config.dreaming`.
|
||||
- [Memory configuration reference](/reference/memory-config) for `memory.search.*`, `memory.qmd.*`, `memory.citations`, and dreaming config under `plugins.entries.memory-core.config.dreaming`.
|
||||
- [Slash commands](/tools/slash-commands) for the current built-in + bundled command catalog.
|
||||
- Owning channel/plugin pages for channel-specific command surfaces.
|
||||
|
||||
@@ -374,7 +374,8 @@ read, account-wide exposure fails closed.
|
||||
- `model`: optional Dream Diary subagent model override. Requires `plugins.entries.memory-core.subagent.allowModelOverride: true`; pair with `allowedModels` to restrict targets. Model-unavailable errors retry once with the session default model; trust or allowlist failures do not fall back silently.
|
||||
- phase policy and thresholds are implementation details (not user-facing config keys).
|
||||
- Full memory config lives in [Memory configuration reference](/reference/memory-config):
|
||||
- `agents.defaults.memorySearch.*`
|
||||
- `memory.search.*`
|
||||
- `agents.entries.*.memory.search.*` for per-agent overrides
|
||||
- `memory.backend`
|
||||
- `memory.citations`
|
||||
- `memory.qmd.*`
|
||||
@@ -387,17 +388,6 @@ See [Plugins](/tools/plugin).
|
||||
|
||||
---
|
||||
|
||||
## Commitments
|
||||
|
||||
`commitments` controls inferred follow-up memory: OpenClaw can detect check-ins from conversation turns and deliver them through heartbeat runs.
|
||||
|
||||
- `commitments.enabled`: enable hidden LLM extraction, storage, and heartbeat delivery for inferred follow-up commitments. Default: `false`.
|
||||
- `commitments.maxPerDay`: maximum inferred follow-up commitments delivered per agent session in a rolling day. Default: `3`.
|
||||
|
||||
See [Inferred commitments](/concepts/commitments).
|
||||
|
||||
---
|
||||
|
||||
## Browser
|
||||
|
||||
```json5
|
||||
@@ -578,8 +568,6 @@ See [Inferred commitments](/concepts/commitments).
|
||||
// chatMessageMaxWidth: "min(1280px, 82%)", // optional centered chat transcript max-width
|
||||
// allowedOrigins: ["https://control.example.com"], // required for non-loopback Control UI
|
||||
// dangerouslyAllowHostHeaderOriginFallback: false, // dangerous Host-header origin fallback mode
|
||||
// allowInsecureAuth: false,
|
||||
// dangerouslyDisableDeviceAuth: false,
|
||||
},
|
||||
terminal: {
|
||||
enabled: false,
|
||||
@@ -605,8 +593,10 @@ See [Inferred commitments](/concepts/commitments).
|
||||
// timeoutMs, cidrs }.
|
||||
sshVerify: true,
|
||||
},
|
||||
allowCommands: ["canvas.navigate"],
|
||||
denyCommands: ["system.run"],
|
||||
commands: {
|
||||
allow: ["canvas.navigate"],
|
||||
deny: ["system.run"],
|
||||
},
|
||||
},
|
||||
tools: {
|
||||
// Additional /tools/invoke HTTP denies
|
||||
@@ -680,7 +670,7 @@ See [Inferred commitments](/concepts/commitments).
|
||||
- `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 `commands.allow` to opt into dangerous node commands such as `camera.snap`, `camera.clip`, `screen.record`, `health.summary`, `sms.search`, and `sms.send`; `commands.deny` 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`
|
||||
@@ -1371,26 +1361,6 @@ writer is best-effort, not a lossless compliance archive.
|
||||
|
||||
---
|
||||
|
||||
## CLI
|
||||
|
||||
```json5
|
||||
{
|
||||
cli: {
|
||||
banner: {
|
||||
taglineMode: "off", // random | default | off
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `cli.banner.taglineMode` controls banner tagline style:
|
||||
- `"random"` (default): rotating funny/seasonal taglines.
|
||||
- `"default"`: fixed neutral tagline (`All your chats, one OpenClaw.`).
|
||||
- `"off"`: no tagline text (banner title/version still shown).
|
||||
- To hide the entire banner (not just taglines), set env `OPENCLAW_HIDE_BANNER=1`.
|
||||
|
||||
---
|
||||
|
||||
## Wizard
|
||||
|
||||
Behavior and metadata for CLI guided setup flows (`onboard`, `configure`, `doctor`):
|
||||
@@ -1418,7 +1388,7 @@ Behavior and metadata for CLI guided setup flows (`onboard`, `configure`, `docto
|
||||
|
||||
## Identity
|
||||
|
||||
See `agents.list` identity fields under [Agent defaults](/gateway/config-agents#agent-defaults).
|
||||
See `agents.entries` identity fields under [Agent defaults](/gateway/config-agents#agent-defaults).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ Common reasons to add a config:
|
||||
|
||||
See the [full reference](/gateway/configuration-reference) for every available field.
|
||||
|
||||
Configuration follows a two-bucket rule: root siblings hold infrastructure and cross-agent defaults, while `agents.defaults` holds agent-loop behavior. Entries under `agents.entries` may override either bucket where the schema supports a per-agent override.
|
||||
|
||||
Agents and automation should use `config.schema.lookup` for exact field-level
|
||||
docs before editing config. Use this page for task-oriented guidance and
|
||||
[Configuration reference](/gateway/configuration-reference) for the broader
|
||||
@@ -213,7 +215,7 @@ candidate contains a redacted secret placeholder such as `***` or `[redacted]`.
|
||||
|
||||
<Accordion title="Restrict skills per agent">
|
||||
Use `agents.defaults.skills` for a shared baseline, then override specific
|
||||
agents with `agents.list[].skills`:
|
||||
agents with `agents.entries.*.skills`:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -231,8 +233,8 @@ candidate contains a redacted secret placeholder such as `***` or `[redacted]`.
|
||||
```
|
||||
|
||||
- Omit `agents.defaults.skills` for unrestricted skills by default.
|
||||
- Omit `agents.list[].skills` to inherit the defaults.
|
||||
- Set `agents.list[].skills: []` for no skills.
|
||||
- Omit `agents.entries.*.skills` to inherit the defaults.
|
||||
- Set `agents.entries.*.skills: []` for no skills.
|
||||
- See [Skills](/tools/skills), [Skills config](/tools/skills-config), and
|
||||
the [Configuration Reference](/gateway/config-agents#agents-defaults-skills).
|
||||
|
||||
@@ -610,7 +612,7 @@ config file already exists (a first write with no existing config skips the chec
|
||||
replacement is intentional. If a patch would replace or delete an existing array
|
||||
with fewer entries, the Gateway rejects the write unless that exact path appears
|
||||
in `replacePaths`; nested arrays under array entries use `[]`, such as
|
||||
`agents.list[].skills`. This prevents truncated `config.get` snapshots from
|
||||
`agents.entries.*.skills`. This prevents truncated `config.get` snapshots from
|
||||
silently clobbering routing or allowlist arrays. Use `config.apply` when you
|
||||
intend to replace the full config.
|
||||
|
||||
@@ -689,7 +691,7 @@ Rules:
|
||||
},
|
||||
channels: {
|
||||
googlechat: {
|
||||
serviceAccountRef: {
|
||||
serviceAccount: {
|
||||
source: "exec",
|
||||
provider: "vault",
|
||||
id: "channels/googlechat/serviceAccount",
|
||||
|
||||
+37
-7
@@ -163,7 +163,7 @@ Flags:
|
||||
- Legacy on-disk state migration (sessions/agent dir/WhatsApp auth).
|
||||
- Legacy plugin manifest contract key migration (`speechProviders`, `realtimeTranscriptionProviders`, `realtimeVoiceProviders`, `mediaUnderstandingProviders`, `imageGenerationProviders`, `videoGenerationProviders`, `webFetchProviders`, `webSearchProviders` → `contracts`).
|
||||
- Legacy cron store migration (`jobId`, `schedule.cron`, top-level delivery/payload fields, payload `provider`, `notify: true` webhook fallback jobs).
|
||||
- Codex CLI runtime pin repair (`agentRuntime.id: "codex-cli"` → `"codex"`) across `agents.defaults`, `agents.list[]`, and `models.providers.*` (including per-model entries).
|
||||
- Codex CLI runtime pin repair (`agentRuntime.id: "codex-cli"` → `"codex"`) across `agents.defaults`, `agents.entries.*`, and `models.providers.*` (including per-model entries).
|
||||
- Stale plugin config cleanup when plugins are enabled; when `plugins.enabled=false`, stale plugin references are preserved as inert containment config.
|
||||
|
||||
</Accordion>
|
||||
@@ -267,8 +267,14 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
| `session.threadBindings.ttlHours`, `channels.<id>.threadBindings.ttlHours` (and per-account) | `...threadBindings.idleHours` |
|
||||
| legacy `talk.voiceId`/`talk.voiceAliases`/`talk.modelId`/`talk.outputFormat`/`talk.apiKey` | `talk.provider` + `talk.providers.<provider>` |
|
||||
| legacy top-level realtime Talk selectors (`talk.mode`/`talk.transport`/`talk.brain`/`talk.model`/`talk.voice`) | `talk.realtime` |
|
||||
| `messages.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) | `messages.tts.providers.<provider>` |
|
||||
| `messages.tts.provider: "edge"` / `messages.tts.providers.edge` | `messages.tts.provider: "microsoft"` / `messages.tts.providers.microsoft` |
|
||||
| `messages.tts` | top-level `tts` |
|
||||
| `messages.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) | `tts.providers.<provider>` |
|
||||
| `messages.tts.provider: "edge"` / `messages.tts.providers.edge` | `tts.provider: "microsoft"` / `tts.providers.microsoft` |
|
||||
| `tools.exec.security` + `tools.exec.ask` | `tools.exec.mode` |
|
||||
| `session.idleMinutes` | `session.reset.idleMinutes` |
|
||||
| `messages.responsePrefix` with explicit channel blocks | copied to configured channel/account `responsePrefix`; global fallback retained for implicit/custom channels |
|
||||
| `web.enabled` | `channels.whatsapp.enabled` |
|
||||
| `meta.lastTouchedAt`, hook installs, cron store, bundled discovery, global TTS prefs path | shared SQLite state |
|
||||
| TTS speaker fields `voice`/`voiceName`/`voiceId` | `speakerVoice`/`speakerVoiceId` |
|
||||
| `channels.<id>.tts.<provider>` / `channels.<id>.accounts.<accountId>.tts.<provider>` (all channels except Discord) | `...tts.providers.<provider>` |
|
||||
| `channels.<id>.voice.tts.<provider>` / `channels.<id>.accounts.<accountId>.voice.tts.<provider>` (all channels, including Discord) | `...voice.tts.providers.<provider>` |
|
||||
@@ -285,8 +291,31 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
| `mcp.servers.*.type` (CLI-native aliases) | `mcp.servers.*.transport` |
|
||||
| `mcp.servers.*.disabled` | inverse `mcp.servers.*.enabled` |
|
||||
| MCP timeout aliases `connectTimeout`/`connect_timeout`/`timeout` | `connectionTimeoutMs`/`requestTimeoutMs` |
|
||||
| MCP snake-case server fields | camelCase MCP server fields |
|
||||
| `tools.media.image/audio/video.models` | capability-tagged `tools.media.models` |
|
||||
| `tools.media.asyncCompletion` | removed |
|
||||
| `tools.message.allowCrossContextSend` | `tools.message.crossContext` |
|
||||
| media model `deepgram` options | `providerOptions.deepgram` |
|
||||
| `talk.realtime.voice`, Discord realtime `voice` | `speakerVoice` |
|
||||
| `agents.defaults.pdfMaxBytesMb` | `agents.defaults.pdfMaxMb` |
|
||||
| `tools.exec.timeoutSec` | `tools.exec.timeoutSeconds` |
|
||||
| `browser.ssrfPolicy.hostnameAllowlist` | wildcard-aware `browser.ssrfPolicy.allowedHostnames` |
|
||||
| sandbox browser `enableNoVnc` | `noVncEnabled` |
|
||||
| root `media` | `attachments` |
|
||||
| channel/account `heartbeat` visibility blocks | `heartbeatVisibility` |
|
||||
| `channels.slack.identity` | `channels.slack.postAs` |
|
||||
| root `audit` | `logging.audit` |
|
||||
| `gateway.nodes.skills.enabled` | `gateway.nodes.allowSkills` |
|
||||
| `gateway.nodes.allowCommands`/`denyCommands` | `gateway.nodes.commands.allow`/`deny` |
|
||||
| generation model defaults | `agents.defaults.mediaModels.{image,video,music}` |
|
||||
| retired final-layout tuning knobs | built-in default behavior |
|
||||
| `channels.whatsapp.messagePrefix` and legacy `messages.messagePrefix` | `channels.whatsapp.responsePrefix` |
|
||||
| `channels.whatsapp.ackReaction` | global `messages.ackReaction` and `ackReactionScope` where translatable |
|
||||
| `cron.failureDestination` | destination fields on `cron.failureAlert` |
|
||||
| `gateway.controlUi.chatMessageMaxWidth` | `ui.prefs.chatMessageMaxWidth` |
|
||||
| `agents.list` | keyed `agents.entries` |
|
||||
| top-level `defaultModel` | `agents.defaults.model` |
|
||||
| `messages.messagePrefix` | `channels.whatsapp.messagePrefix` |
|
||||
| `messages.messagePrefix` | `channels.whatsapp.responsePrefix` |
|
||||
| `session.maintenance.pruneDays`, `session.resetByType.dm` | `session.maintenance.pruneAfter`, `session.resetByType.direct` |
|
||||
| top-level `tui` | removed (the TUI footer uses the compact default) |
|
||||
| `plugins.entries.codex.config.codexDynamicToolsProfile` | removed (Codex app-server always keeps Codex-native workspace tools native) |
|
||||
@@ -296,7 +325,8 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
| `agents.defaults/list[].embeddedPi` | `embeddedAgent` |
|
||||
| `agents.defaults/list[].sandbox.perSession` | `sandbox.scope` |
|
||||
| `agents.defaults.llm` | removed (use `models.providers.<id>.timeoutSeconds` for slow model/provider timeouts, kept below the agent/run timeout ceiling) |
|
||||
| top-level `memorySearch` | `agents.defaults.memorySearch` |
|
||||
| top-level `memorySearch`, `agents.defaults.memorySearch` | `memory.search` |
|
||||
| `agents.entries.*.memorySearch` | `agents.entries.*.memory.search` |
|
||||
| `memorySearch.provider: "auto"` | `"openai"` |
|
||||
| `memorySearch.store.path` (any level) | removed (memory indexes live in each agent database) |
|
||||
| top-level `heartbeat` | `agents.defaults.heartbeat` / `channels.defaults.heartbeat` |
|
||||
@@ -354,7 +384,7 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
- Existing provider/model runtime policy is preserved unless the repaired legacy model ref needs Codex routing to keep the old auth path.
|
||||
- Existing model fallback lists are preserved with their legacy entries rewritten; copied per-model settings move from the legacy key to the canonical `openai/*` key.
|
||||
- Persisted session `modelProvider`/`providerOverride`, `model`/`modelOverride`, fallback notices, and auth-profile pins are repaired across all discovered agent session stores.
|
||||
- Doctor separately repairs stale `agentRuntime.id: "codex-cli"` pins (a distinct legacy runtime id) to `"codex"` across `agents.defaults`, `agents.list[]`, and `models.providers.*` model entries.
|
||||
- Doctor separately repairs stale `agentRuntime.id: "codex-cli"` pins (a distinct legacy runtime id) to `"codex"` across `agents.defaults`, `agents.entries.*`, and `models.providers.*` model entries.
|
||||
- `/codex ...` means "control or bind a native Codex conversation from chat."
|
||||
- `/acp ...` or `runtime: "acp"` means "use the external ACP/acpx adapter."
|
||||
|
||||
@@ -380,7 +410,7 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
Doctor scans all installed plugin manifests for deprecated top-level capability keys (`speechProviders`, `realtimeTranscriptionProviders`, `realtimeVoiceProviders`, `mediaUnderstandingProviders`, `imageGenerationProviders`, `videoGenerationProviders`, `webFetchProviders`, `webSearchProviders`). When found, it offers to move them into the `contracts` object and rewrite the manifest file in-place. This migration is idempotent; if `contracts` already has the same values, the legacy key is removed without duplicating data.
|
||||
</Accordion>
|
||||
<Accordion title="3b. Legacy cron store migrations">
|
||||
Doctor also checks the cron job store (`~/.openclaw/cron/jobs.json` by default, or `cron.store` when overridden) for old job shapes that the scheduler still accepts for compatibility.
|
||||
Doctor also checks the legacy cron job store (`~/.openclaw/cron/jobs.json`) for old job shapes before importing canonical rows into SQLite.
|
||||
|
||||
Current cron cleanups include:
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ When no `x-openclaw-session-key` header or `user` field is provided, `/v1/chat/c
|
||||
|
||||
- `logged out` or status 409-515 -> relink with `openclaw channels logout` then `openclaw channels login`.
|
||||
- Gateway unreachable -> start it: `openclaw gateway --port 18789` (use `--force` if the port is busy).
|
||||
- No inbound messages -> confirm linked phone is online and the sender is allowed (`channels.whatsapp.allowFrom`); for group chats, ensure allowlist + mention rules match (`channels.whatsapp.groups`, `agents.list[].groupChat.mentionPatterns`).
|
||||
- No inbound messages -> confirm linked phone is online and the sender is allowed (`channels.whatsapp.allowFrom`); for group chats, ensure allowlist + mention rules match (`channels.whatsapp.groups`, `agents.entries.*.groupChat.mentionPatterns`).
|
||||
|
||||
## Dedicated "health" command
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@ Example config:
|
||||
|
||||
## Defaults
|
||||
|
||||
- Interval: `30m`. Applying Anthropic provider defaults bumps this to `1h` when the resolved auth mode is OAuth/token (including Claude CLI reuse), but only while `heartbeat.every` is unset. Set `agents.defaults.heartbeat.every` or per-agent `agents.list[].heartbeat.every`; use `0m` to disable.
|
||||
- Interval: `30m`. Applying Anthropic provider defaults bumps this to `1h` when the resolved auth mode is OAuth/token (including Claude CLI reuse), but only while `heartbeat.every` is unset. Set `agents.defaults.heartbeat.every` or per-agent `agents.entries.*.heartbeat.every`; use `0m` to disable.
|
||||
- Prompt body (configurable via `agents.defaults.heartbeat.prompt`): `Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.`
|
||||
- Timeout: unset heartbeat turns use `agents.defaults.timeoutSeconds` when set. Otherwise, they use the heartbeat cadence capped at 600 seconds. Set `agents.defaults.heartbeat.timeoutSeconds` or per-agent `agents.list[].heartbeat.timeoutSeconds` for longer heartbeat work.
|
||||
- Timeout: unset heartbeat turns use `agents.defaults.timeoutSeconds` when set. Otherwise, they use the heartbeat cadence capped at 600 seconds. Set `agents.defaults.heartbeat.timeoutSeconds` or per-agent `agents.entries.*.heartbeat.timeoutSeconds` for longer heartbeat work.
|
||||
- The heartbeat prompt is sent **verbatim** as the user message. The system prompt includes a "Heartbeats" section only when heartbeats are enabled for the default agent (and `includeSystemPromptSection` is not `false`), and the run is flagged internally.
|
||||
- When heartbeats are disabled with `0m`, normal runs also omit `HEARTBEAT.md` from bootstrap context so the model does not see heartbeat-only instructions.
|
||||
- Active hours (`heartbeat.activeHours`) are checked in the configured timezone. Outside the window, heartbeats are skipped until the next tick inside the window.
|
||||
@@ -78,7 +78,7 @@ The default prompt is intentionally broad:
|
||||
|
||||
Heartbeat can react to completed [background tasks](/automation/tasks), but a heartbeat run itself does not create a task record.
|
||||
|
||||
If you want a heartbeat to do something very specific (e.g. "check Gmail PubSub stats" or "verify gateway health"), set `agents.defaults.heartbeat.prompt` (or `agents.list[].heartbeat.prompt`) to a custom body (sent verbatim).
|
||||
If you want a heartbeat to do something very specific (e.g. "check Gmail PubSub stats" or "verify gateway health"), set `agents.defaults.heartbeat.prompt` (or `agents.entries.*.heartbeat.prompt`) to a custom body (sent verbatim).
|
||||
|
||||
## Response contract
|
||||
|
||||
@@ -119,14 +119,14 @@ Outside heartbeats, stray `HEARTBEAT_OK` at the start/end of a message is stripp
|
||||
### Scope and precedence
|
||||
|
||||
- `agents.defaults.heartbeat` sets global heartbeat behavior.
|
||||
- `agents.list[].heartbeat` merges on top; if any agent has a `heartbeat` block, **only those agents** run heartbeats.
|
||||
- `channels.defaults.heartbeat` sets visibility defaults for all channels.
|
||||
- `channels.<channel>.heartbeat` overrides channel defaults.
|
||||
- `channels.<channel>.accounts.<id>.heartbeat` (multi-account channels) overrides per-channel settings.
|
||||
- `agents.entries.*.heartbeat` merges on top; if any agent has a `heartbeat` block, **only those agents** run heartbeats.
|
||||
- `channels.defaults.heartbeatVisibility` sets visibility defaults for all channels.
|
||||
- `channels.<channel>.heartbeatVisibility` overrides channel defaults.
|
||||
- `channels.<channel>.accounts.<id>.heartbeatVisibility` (multi-account channels) overrides per-channel settings.
|
||||
|
||||
### Per-agent heartbeats
|
||||
|
||||
If any `agents.list[]` entry includes a `heartbeat` block, **only those agents** run heartbeats. The per-agent block merges on top of `agents.defaults.heartbeat` (so you can set shared defaults once and override per agent).
|
||||
If any `agents.entries.*` entry includes a `heartbeat` block, **only those agents** run heartbeats. The per-agent block merges on top of `agents.defaults.heartbeat` (so you can set shared defaults once and override per agent).
|
||||
|
||||
Example: two agents, only the second agent runs heartbeats.
|
||||
|
||||
|
||||
@@ -58,13 +58,13 @@ Tune console verbosity independently:
|
||||
|
||||
OpenClaw masks sensitive tokens before log or transcript output leaves the process. This redaction policy applies at console, file-log, OTLP log-record, and session transcript text sinks, so matching secret values are masked before JSONL lines or messages are written to disk.
|
||||
|
||||
- `logging.redactSensitive`: `off` | `tools` (default: `tools`)
|
||||
- Sensitive-value redaction is always enabled.
|
||||
- `logging.redactPatterns`: array of regex strings (overrides defaults)
|
||||
- Use raw regex strings (auto `gi`), or `/pattern/flags` for custom flags.
|
||||
- Matches are masked keeping the first 6 + last 4 chars (values >= 18 chars); shorter values become `***`.
|
||||
- Defaults cover common key assignments, CLI flags, JSON fields, bearer headers, PEM blocks, popular vendor token prefixes, and payment credential field names (card number, CVC/CVV, shared payment token, payment credential).
|
||||
|
||||
Some safety boundaries always redact regardless of `logging.redactSensitive`: Control UI tool-call events, `sessions_history` tool output, diagnostics support exports, provider error observations, exec approval command display, and Gateway WebSocket protocol logs. These surfaces still honor `logging.redactPatterns` as additional patterns, but `redactSensitive: "off"` does not make them emit raw secrets.
|
||||
Safety boundaries such as Control UI tool-call events, `sessions_history` output, diagnostics exports, provider errors, exec approval display, and Gateway WebSocket logs always redact. `logging.redactPatterns` adds deployment-specific patterns.
|
||||
|
||||
## Gateway WebSocket logs
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ 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
|
||||
`denyCommands`).
|
||||
the gateway's global node command policy (`gateway.nodes.commands.allow` and
|
||||
`gateway.nodes.commands.deny`).
|
||||
- Per-node `system.run` allow and ask policy lives on the node in
|
||||
`exec.approvals.node.*`, not in the pairing record.
|
||||
|
||||
|
||||
@@ -355,7 +355,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
|
||||
|
||||
@@ -531,7 +531,7 @@ methods. Treat this as feature discovery, not a full enumeration of
|
||||
- `tts.enable` and `tts.disable` toggle TTS prefs state.
|
||||
- `tts.setProvider` updates the preferred TTS provider.
|
||||
- `tts.convert` runs one-shot text-to-speech conversion.
|
||||
- `tts.speak` (`operator.write`) renders non-empty `text` with the configured general TTS provider chain and returns one whole clip inline as `audioBase64`, plus `provider` and optional `outputFormat`, `mimeType`, and `fileExtension` metadata. Unlike `tts.convert`, it does not return a Gateway-local path; unlike `talk.speak`, it does not require a Talk provider. Text above `messages.tts.maxTextLength` returns `INVALID_REQUEST`; synthesis failures return `UNAVAILABLE`.
|
||||
- `tts.speak` (`operator.write`) renders non-empty `text` with the configured general TTS provider chain and returns one whole clip inline as `audioBase64`, plus `provider` and optional `outputFormat`, `mimeType`, and `fileExtension` metadata. Unlike `tts.convert`, it does not return a Gateway-local path; unlike `talk.speak`, it does not require a Talk provider. Text above `tts.maxTextLength` returns `INVALID_REQUEST`; synthesis failures return `UNAVAILABLE`.
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -540,7 +540,7 @@ methods. Treat this as feature discovery, not a full enumeration of
|
||||
- `secrets.resolve` resolves command-target secret assignments for a specific command/target set.
|
||||
- `config.get` returns the current on-disk config snapshot, raw root-file `hash`, resolved `configRevisionHash`, and optional `appliedConfigHash` for the resolved revision accepted by the active Gateway runtime.
|
||||
- `config.set` writes a validated config payload.
|
||||
- `config.patch` merges a partial config update. Destructive array replacement requires the affected path in `replacePaths`; nested arrays under array entries use `[]` paths such as `agents.list[].skills`.
|
||||
- `config.patch` merges a partial config update. Destructive array replacement requires the affected path in `replacePaths`; nested arrays under array entries use `[]` paths such as `agents.entries.*.skills`.
|
||||
- `config.apply` validates + replaces the full config payload.
|
||||
- `config.schema` returns the live config schema payload used by Control UI and CLI tooling: schema, `uiHints`, version, generation metadata, plugin + channel schema metadata when loadable. It includes `title` / `description` metadata from the same labels/help text as the UI, including nested object, wildcard, array-item, and `anyOf` / `oneOf` / `allOf` composition branches when matching field documentation exists.
|
||||
- `config.schema.lookup` returns a path-scoped lookup payload for one config path: normalized path, a shallow schema node, matched hint + `hintPath`, optional `reloadKind`, and immediate child summaries for UI/CLI drill-down. `reloadKind` is one of `restart`, `hot`, or `none` (`src/config/schema.ts`) and mirrors the gateway config reload planner for the requested path. Lookup schema nodes keep the user-facing docs and common validation fields (`title`, `description`, `type`, `enum`, `const`, `format`, `pattern`, numeric/string/array/object bounds, `additionalProperties`, `deprecated`, `readOnly`, `writeOnly`). Child summaries expose `key`, normalized `path`, `type`, `required`, `hasChildren`, optional `reloadKind`, plus the matched `hint` / `hintPath`.
|
||||
@@ -607,7 +607,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.
|
||||
@@ -1107,11 +1107,7 @@ not replay rejected requests after reconnecting.
|
||||
and require approval.
|
||||
- WS clients normally include `device` identity during `connect` (operator +
|
||||
node). The only device-less operator exceptions are explicit trust paths:
|
||||
- `gateway.controlUi.allowInsecureAuth=true` for localhost-only insecure
|
||||
HTTP compatibility.
|
||||
- successful `gateway.auth.mode: "trusted-proxy"` operator Control UI auth.
|
||||
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true` (break-glass, severe
|
||||
security downgrade).
|
||||
- direct-loopback `gateway-client` backend RPCs on the reserved internal
|
||||
helper path.
|
||||
- Omitting device identity has scope consequences. When a device-less
|
||||
@@ -1119,9 +1115,6 @@ not replay rejected requests after reconnecting.
|
||||
still clears self-declared scopes to an empty set unless that path has a
|
||||
named scope-preservation exception. Scope-gated methods then fail with
|
||||
`missing scope`.
|
||||
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true` is a Control UI
|
||||
break-glass scope-preservation path. It does not grant scopes to arbitrary
|
||||
custom backend or CLI-shaped WebSocket clients.
|
||||
- The reserved direct-loopback `gateway-client` backend helper path preserves
|
||||
scopes only for internal local control-plane RPCs; custom backend IDs do
|
||||
not receive this exception.
|
||||
|
||||
@@ -7,9 +7,9 @@ status: active
|
||||
|
||||
OpenClaw has three related but different controls:
|
||||
|
||||
1. **Sandbox** (`agents.defaults.sandbox.*` / `agents.list[].sandbox.*`) decides **where tools run** (sandbox backend vs host).
|
||||
2. **Tool policy** (`tools.*`, `tools.sandbox.tools.*`, `agents.list[].tools.*`) decides **which tools are available/allowed**.
|
||||
3. **Elevated** (`tools.elevated.*`, `agents.list[].tools.elevated.*`) is an **exec-only escape hatch** to run outside the sandbox when you are sandboxed (`gateway` by default, or `node` when the exec target is configured to `node`).
|
||||
1. **Sandbox** (`agents.defaults.sandbox.*` / `agents.entries.*.sandbox.*`) decides **where tools run** (sandbox backend vs host).
|
||||
2. **Tool policy** (`tools.*`, `tools.sandbox.tools.*`, `agents.entries.*.tools.*`) decides **which tools are available/allowed**.
|
||||
3. **Elevated** (`tools.elevated.*`, `agents.entries.*.tools.elevated.*`) is an **exec-only escape hatch** to run outside the sandbox when you are sandboxed (`gateway` by default, or `node` when the exec target is configured to `node`).
|
||||
|
||||
## Quick debug
|
||||
|
||||
@@ -57,11 +57,11 @@ For a per-agent configuration with several host folders, access modes, and the e
|
||||
|
||||
Two layers matter:
|
||||
|
||||
- **Tool profile**: `tools.profile` and `agents.list[].tools.profile` (base allowlist)
|
||||
- **Provider tool profile**: `tools.byProvider[provider].profile` and `agents.list[].tools.byProvider[provider].profile`
|
||||
- **Global/per-agent tool policy**: `tools.allow`/`tools.deny` and `agents.list[].tools.allow`/`agents.list[].tools.deny`
|
||||
- **Provider tool policy**: `tools.byProvider[provider].allow/deny` and `agents.list[].tools.byProvider[provider].allow/deny`
|
||||
- **Sandbox tool policy** (only applies when sandboxed): `tools.sandbox.tools.allow`/`tools.sandbox.tools.deny` and `agents.list[].tools.sandbox.tools.*`
|
||||
- **Tool profile**: `tools.profile` and `agents.entries.*.tools.profile` (base allowlist)
|
||||
- **Provider tool profile**: `tools.byProvider[provider].profile` and `agents.entries.*.tools.byProvider[provider].profile`
|
||||
- **Global/per-agent tool policy**: `tools.allow`/`tools.deny` and `agents.entries.*.tools.allow`/`agents.entries.*.tools.deny`
|
||||
- **Provider tool policy**: `tools.byProvider[provider].allow/deny` and `agents.entries.*.tools.byProvider[provider].allow/deny`
|
||||
- **Sandbox tool policy** (only applies when sandboxed): `tools.sandbox.tools.allow`/`tools.sandbox.tools.deny` and `agents.entries.*.tools.sandbox.tools.*`
|
||||
|
||||
Rules of thumb:
|
||||
|
||||
@@ -126,8 +126,8 @@ Elevated does **not** grant extra tools; it only affects `exec`.
|
||||
|
||||
Gates:
|
||||
|
||||
- Enablement: `tools.elevated.enabled` (and optionally `agents.list[].tools.elevated.enabled`)
|
||||
- Sender allowlists: `tools.elevated.allowFrom.<provider>` (and optionally `agents.list[].tools.elevated.allowFrom.<provider>`)
|
||||
- Enablement: `tools.elevated.enabled` (and optionally `agents.entries.*.tools.elevated.enabled`)
|
||||
- Sender allowlists: `tools.elevated.allowFrom.<provider>` (and optionally `agents.entries.*.tools.elevated.allowFrom.<provider>`)
|
||||
|
||||
See [Elevated Mode](/tools/elevated).
|
||||
|
||||
@@ -137,9 +137,9 @@ See [Elevated Mode](/tools/elevated).
|
||||
|
||||
Fix-it keys (pick one):
|
||||
|
||||
- Disable sandbox: `agents.defaults.sandbox.mode=off` (or per-agent `agents.list[].sandbox.mode=off`)
|
||||
- Disable sandbox: `agents.defaults.sandbox.mode=off` (or per-agent `agents.entries.*.sandbox.mode=off`)
|
||||
- Allow the tool inside sandbox:
|
||||
- remove it from `tools.sandbox.tools.deny` (or per-agent `agents.list[].tools.sandbox.tools.deny`)
|
||||
- remove it from `tools.sandbox.tools.deny` (or per-agent `agents.entries.*.tools.sandbox.tools.deny`)
|
||||
- or add it to `tools.sandbox.tools.allow` (or per-agent allow)
|
||||
- Check `openclaw logs` for the `agents/tool-policy` entry. It records the sandbox mode and whether the allow or deny rule blocked the tool.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ read_when: "You want a dedicated explanation of sandboxing or need to tune agent
|
||||
status: active
|
||||
---
|
||||
|
||||
OpenClaw can run tool execution inside a sandbox backend to reduce blast radius. Sandboxing is off by default and controlled by `agents.defaults.sandbox` (global) or `agents.list[].sandbox` (per-agent). The Gateway process always stays on the host; only tool execution moves into the sandbox when enabled.
|
||||
OpenClaw can run tool execution inside a sandbox backend to reduce blast radius. Sandboxing is off by default and controlled by `agents.defaults.sandbox` (global) or `agents.entries.*.sandbox` (per-agent). The Gateway process always stays on the host; only tool execution moves into the sandbox when enabled.
|
||||
|
||||
<Note>
|
||||
This is not a perfect security boundary, but it materially limits filesystem and process access when the model does something dumb.
|
||||
@@ -396,7 +396,7 @@ For Docker gateway deployments, `scripts/docker/setup.sh` can bootstrap sandbox
|
||||
Paths:
|
||||
|
||||
- Global: `agents.defaults.sandbox.docker.setupCommand`
|
||||
- Per-agent: `agents.list[].sandbox.docker.setupCommand`
|
||||
- Per-agent: `agents.entries.*.sandbox.docker.setupCommand`
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Common pitfalls">
|
||||
@@ -425,7 +425,7 @@ Debugging:
|
||||
|
||||
## Multi-agent overrides
|
||||
|
||||
Each agent can override sandbox + tools: `agents.list[].sandbox` and `agents.list[].tools` (plus `agents.list[].tools.sandbox.tools` for sandbox tool policy). See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for precedence.
|
||||
Each agent can override sandbox + tools: `agents.entries.*.sandbox` and `agents.entries.*.tools` (plus `agents.entries.*.tools.sandbox.tools` for sandbox tool policy). See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for precedence.
|
||||
|
||||
## Minimal enable example
|
||||
|
||||
|
||||
@@ -575,7 +575,7 @@ Warning and audit signals:
|
||||
- `SECRETS_REF_OVERRIDES_PLAINTEXT` (runtime warning)
|
||||
- `REF_SHADOWED` (audit finding when `auth-profiles.json` credentials take precedence over `openclaw.json` refs)
|
||||
|
||||
Google Chat compatibility: `serviceAccountRef` takes precedence over plaintext `serviceAccount`; the plaintext value is ignored once the sibling ref is set.
|
||||
Google Chat `serviceAccount` accepts inline JSON or a SecretRef. Doctor moves the retired sibling `serviceAccountRef` into this canonical field when it is unset.
|
||||
|
||||
## Activation triggers
|
||||
|
||||
|
||||
@@ -21,127 +21,127 @@ either level depending on config (for example, whether the Gateway is remotely
|
||||
exposed). High-signal values you will most likely see in real deployments (not
|
||||
exhaustive):
|
||||
|
||||
| `checkId` | Severity | Why it matters | Primary fix key/path | Auto-fix |
|
||||
| --------------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -------- |
|
||||
| `fs.state_dir.perms_world_writable` | critical | Other users/processes can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
|
||||
| `fs.state_dir.perms_group_writable` | warn | Group users can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
|
||||
| `fs.state_dir.perms_readable` | warn | State dir is readable by others | filesystem perms on `~/.openclaw` | yes |
|
||||
| `fs.state_dir.symlink` | warn | State dir target becomes another trust boundary | state dir filesystem layout | no |
|
||||
| `fs.config.perms_writable` | critical | Others can change auth/tool policy/config | filesystem perms on `~/.openclaw/openclaw.json` | yes |
|
||||
| `fs.config.symlink` | warn | Symlinked config files are unsupported for writes and add another trust boundary | replace with a regular config file or point `OPENCLAW_CONFIG_PATH` at the real file | no |
|
||||
| `fs.config.perms_group_readable` | warn | Group users can read config tokens/settings | filesystem perms on config file | yes |
|
||||
| `fs.config.perms_world_readable` | critical | Config can expose tokens/settings | filesystem perms on config file | yes |
|
||||
| `fs.config_include.perms_writable` | critical | Config include file can be modified by others | include-file perms referenced from `openclaw.json` | yes |
|
||||
| `fs.config_include.perms_group_readable` | warn | Group users can read included secrets/settings | include-file perms referenced from `openclaw.json` | yes |
|
||||
| `fs.config_include.perms_world_readable` | critical | Included secrets/settings are world-readable | include-file perms referenced from `openclaw.json` | yes |
|
||||
| `fs.auth_profiles.perms_writable` | critical | Others can inject or replace stored model credentials | `agents/<agentId>/agent/auth-profiles.json` perms | yes |
|
||||
| `fs.auth_profiles.perms_readable` | warn | Others can read API keys and OAuth tokens | `agents/<agentId>/agent/auth-profiles.json` perms | yes |
|
||||
| `fs.credentials_dir.perms_writable` | critical | Others can modify channel pairing/credential state | filesystem perms on `~/.openclaw/credentials` | yes |
|
||||
| `fs.credentials_dir.perms_readable` | warn | Others can read channel credential state | filesystem perms on `~/.openclaw/credentials` | yes |
|
||||
| `fs.sessions_store.perms_readable` | warn | Others can read session transcripts/metadata | session store perms | yes |
|
||||
| `fs.log_file.perms_readable` | warn | Others can read redacted-but-still-sensitive logs | gateway log file perms | yes |
|
||||
| `fs.synced_dir` | warn | State/config in iCloud/Dropbox/Drive broadens token/transcript exposure | move config/state off synced folders | no |
|
||||
| `gateway.bind_no_auth` | critical | Remote bind without shared secret | `gateway.bind`, `gateway.auth.*` | no |
|
||||
| `gateway.loopback_no_auth` | critical | Reverse-proxied loopback may become unauthenticated | `gateway.auth.*`, proxy setup | no |
|
||||
| `gateway.trusted_proxies_missing` | warn | Reverse-proxy headers are present but not trusted | `gateway.trustedProxies` | no |
|
||||
| `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.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 |
|
||||
| `gateway.control_ui.allowed_origins_wildcard` | warn/critical | `allowedOrigins=["*"]` disables browser-origin allowlisting | `gateway.controlUi.allowedOrigins` | no |
|
||||
| `gateway.control_ui.host_header_origin_fallback` | warn/critical | Enables Host-header origin fallback (DNS rebinding hardening downgrade) | `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback` | no |
|
||||
| `gateway.control_ui.insecure_auth` | warn | Insecure-auth compatibility toggle enabled | `gateway.controlUi.allowInsecureAuth` | no |
|
||||
| `gateway.control_ui.device_auth_disabled` | critical | Disables device identity check | `gateway.controlUi.dangerouslyDisableDeviceAuth` | no |
|
||||
| `gateway.real_ip_fallback_enabled` | warn/critical | Trusting `X-Real-IP` fallback can enable source-IP spoofing via proxy misconfig | `gateway.allowRealIpFallback`, `gateway.trustedProxies` | no |
|
||||
| `gateway.token_too_short` | warn | Short shared token is easier to brute force | `gateway.auth.token` | no |
|
||||
| `gateway.auth_no_rate_limit` | warn | Exposed auth without rate limiting increases brute-force risk | `gateway.auth.rateLimit` | no |
|
||||
| `gateway.trusted_proxy_auth` | critical | Proxy identity now becomes the auth boundary | `gateway.auth.mode="trusted-proxy"` | no |
|
||||
| `gateway.trusted_proxy_no_proxies` | critical | Trusted-proxy auth without trusted proxy IPs is unsafe | `gateway.trustedProxies` | no |
|
||||
| `gateway.trusted_proxy_no_user_header` | critical | Trusted-proxy auth cannot resolve user identity safely | `gateway.auth.trustedProxy.userHeader` | no |
|
||||
| `gateway.trusted_proxy_no_allowlist` | warn | Trusted-proxy auth accepts any authenticated upstream user | `gateway.auth.trustedProxy.allowUsers` | no |
|
||||
| `gateway.trusted_proxy_allow_loopback` | warn | Trusted-proxy auth accepts explicitly allowed loopback proxy sources | `gateway.auth.trustedProxy.allowLoopback` | no |
|
||||
| `gateway.probe_auth_secretref_unavailable` | warn | Deep probe could not resolve auth SecretRefs in this command path | deep-probe auth source / SecretRef availability | no |
|
||||
| `gateway.probe_failed` | warn | Live Gateway probe failed (`--deep` only) | gateway reachability/auth | no |
|
||||
| `discovery.mdns_full_mode` | warn/critical | mDNS full mode advertises `cliPath`/`sshPort` metadata on local network | `discovery.mdns.mode`, `gateway.bind` | no |
|
||||
| `config.insecure_or_dangerous_flags` | warn | One insecure/dangerous debug flag is enabled | key named in finding detail | no |
|
||||
| `security.audit.suppressions.active` | info | Audit output has configured suppressions and may be filtered | `security.audit.suppressions` | no |
|
||||
| `config.secrets.gateway_password_in_config` | warn | Gateway password is stored directly in config | `gateway.auth.password` | no |
|
||||
| `config.secrets.hooks_token_in_config` | warn | Hook bearer token is stored directly in config | `hooks.token` | no |
|
||||
| `hooks.token_reuse_gateway_token` | critical | Hook ingress token also unlocks Gateway auth | `hooks.token`, `gateway.auth.token`, `gateway.auth.password` | no |
|
||||
| `hooks.token_too_short` | warn | Easier brute force on hook ingress | `hooks.token` | no |
|
||||
| `hooks.default_session_key_unset` | warn | Hook agent runs fan out into generated per-request sessions | `hooks.defaultSessionKey` | no |
|
||||
| `hooks.allowed_agent_ids_unrestricted` | warn/critical | Authenticated hook callers may route to any configured agent | `hooks.allowedAgentIds` | no |
|
||||
| `hooks.request_session_key_enabled` | warn/critical | External caller can choose sessionKey | `hooks.allowRequestSessionKey` | no |
|
||||
| `hooks.request_session_key_prefixes_missing` | warn/critical | No bound on external session key shapes | `hooks.allowedSessionKeyPrefixes` | no |
|
||||
| `hooks.path_root` | critical | Hook path is `/`, making ingress easier to collide or misroute | `hooks.path` | no |
|
||||
| `hooks.installs_unpinned_npm_specs` | warn | Hook install records are not pinned to immutable npm specs | hook install metadata | no |
|
||||
| `hooks.installs_missing_integrity` | warn | Hook install records lack integrity metadata | hook install metadata | no |
|
||||
| `hooks.installs_version_drift` | warn | Hook install records drift from installed packages | hook install metadata | no |
|
||||
| `logging.redact_off` | warn | Sensitive values leak to logs/status | `logging.redactSensitive` | yes |
|
||||
| `browser.control_invalid_config` | warn | Browser control config is invalid before runtime | `browser.*` | no |
|
||||
| `browser.control_no_auth` | critical | Browser control exposed without token/password auth | `gateway.auth.*` | no |
|
||||
| `browser.remote_cdp_http` | warn | Remote CDP over plain HTTP lacks transport encryption | browser profile `cdpUrl` | no |
|
||||
| `browser.remote_cdp_private_host` | warn | Remote CDP targets a private/internal host | browser profile `cdpUrl`, `browser.ssrfPolicy.*` | no |
|
||||
| `sandbox.docker_config_mode_off` | warn | Sandbox Docker config present but inactive | `agents.*.sandbox.mode` | no |
|
||||
| `sandbox.bind_mount_non_absolute` | warn | Relative bind mounts can resolve unpredictably | `agents.*.sandbox.docker.binds[]` | no |
|
||||
| `sandbox.dangerous_bind_mount` | critical | Sandbox bind mount targets blocked system, credential, or Docker socket paths | `agents.*.sandbox.docker.binds[]` | no |
|
||||
| `sandbox.dangerous_network_mode` | critical | Sandbox Docker network uses `host` or `container:*` namespace-join mode | `agents.*.sandbox.docker.network` | no |
|
||||
| `sandbox.dangerous_seccomp_profile` | critical | Sandbox seccomp profile weakens container isolation | `agents.*.sandbox.docker.securityOpt` | no |
|
||||
| `sandbox.dangerous_apparmor_profile` | critical | Sandbox AppArmor profile weakens container isolation | `agents.*.sandbox.docker.securityOpt` | no |
|
||||
| `sandbox.browser_cdp_bridge_unrestricted` | warn | Sandbox browser bridge is exposed without source-range restriction | `sandbox.browser.cdpSourceRange` | no |
|
||||
| `sandbox.browser_container.non_loopback_publish` | critical | Existing browser container publishes CDP on non-loopback interfaces | browser sandbox container publish config | no |
|
||||
| `sandbox.browser_container.hash_label_missing` | warn | Existing browser container predates current config-hash labels | `openclaw sandbox recreate --browser --all` | no |
|
||||
| `sandbox.browser_container.hash_epoch_stale` | warn | Existing browser container predates current browser config epoch | `openclaw sandbox recreate --browser --all` | no |
|
||||
| `sandbox.browser_container.docker_probe_timeout` | warn | Docker label probe for the browser container timed out | Docker daemon reachability | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` fails closed when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` fails closed when sandbox is off | `agents.list[].tools.exec.host`, `agents.list[].sandbox.mode` | no |
|
||||
| `tools.exec.security_full_configured` | warn/critical | Host exec is running with `security="full"` | `tools.exec.security`, `agents.list[].tools.exec.security` | no |
|
||||
| `tools.exec.agent_skill_mcp_boundary_drift` | warn | Agent skill allowlists are present while host exec can reach MCP clients/registries | `agents.list[].tools.exec.*`, sandbox/OS isolation, MCP server credentials | no |
|
||||
| `tools.exec.fs_tools_disabled_but_exec_enabled` | warn | Filesystem tool policy does not make shell execution read-only | `tools.deny`, `agents.list[].tools.deny`, `agents.*.sandbox.workspaceAccess` | no |
|
||||
| `tools.exec.auto_allow_skills_enabled` | warn | Exec approvals trust skill bins implicitly | host approvals file | no |
|
||||
| `tools.exec.allowlist_interpreter_without_strict_inline_eval` | warn | Interpreter allowlists permit inline eval without forced reapproval | `tools.exec.strictInlineEval`, `agents.list[].tools.exec.strictInlineEval`, exec approvals allowlist | no |
|
||||
| `tools.exec.safe_bins_interpreter_unprofiled` | warn | Interpreter/runtime bins in `safeBins` without explicit profiles broaden exec risk | `tools.exec.safeBins`, `tools.exec.safeBinProfiles`, `agents.list[].tools.exec.*` | no |
|
||||
| `tools.exec.safe_bins_broad_behavior` | warn | Broad-behavior tools in `safeBins` weaken the low-risk stdin-filter trust model | `tools.exec.safeBins`, `agents.list[].tools.exec.safeBins` | no |
|
||||
| `tools.exec.safe_bin_trusted_dirs_risky` | warn | `safeBinTrustedDirs` includes mutable or risky directories | `tools.exec.safeBinTrustedDirs`, `agents.list[].tools.exec.safeBinTrustedDirs` | no |
|
||||
| `tools.elevated.allowFrom.<provider>.wildcard` | critical | `tools.elevated.allowFrom.<provider>` includes `"*"`, approving every sender | `tools.elevated.allowFrom.<provider>` | no |
|
||||
| `tools.elevated.allowFrom.<provider>.large` | warn | Elevated allowlist for `<provider>` has more than 25 entries | `tools.elevated.allowFrom.<provider>` | no |
|
||||
| `agents.claude_cli.permission_mode_overridden_by_yolo` | warn | Claude CLI `--permission-mode` is ignored because OpenClaw exec is fully unattended | `tools.exec.security`, `tools.exec.ask`, `cliBackends.claude-cli` args | no |
|
||||
| `skills.workspace.symlink_escape` | warn | Workspace `skills/**/SKILL.md` resolves outside workspace root (symlink-chain drift) | workspace `skills/**` filesystem state | no |
|
||||
| `skills.workspace.scan_truncated` | warn | Workspace skill scan hit its directory-visit cap before finishing | flatten/simplify the workspace `skills/` directory tree | no |
|
||||
| `plugins.extensions_no_allowlist` | warn | Plugins are installed without an explicit plugin allowlist | `plugins.allowlist` | no |
|
||||
| `plugins.allow_phantom_entries` | warn | `plugins.allow` lists an ID with no matching installed plugin | `plugins.allow` | no |
|
||||
| `plugins.installs_unpinned_npm_specs` | warn | Plugin index records are not pinned to immutable npm specs | plugin install metadata | no |
|
||||
| `plugins.installs_missing_integrity` | warn | Plugin index records lack integrity metadata | plugin install metadata | no |
|
||||
| `plugins.installs_version_drift` | warn | Plugin index records drift from installed packages | plugin install metadata | no |
|
||||
| `plugins.code_safety` | warn/critical | Plugin code scan found suspicious or dangerous patterns (`--deep` only) | plugin code / install source | no |
|
||||
| `plugins.code_safety.entry_path` | warn | Plugin entry path points into hidden or `node_modules` locations | plugin manifest `entry` | no |
|
||||
| `plugins.code_safety.entry_escape` | critical | Plugin entry escapes the plugin directory | plugin manifest `entry` | no |
|
||||
| `plugins.code_safety.manifest_parse_error` | warn | Plugin manifest could not be parsed during the code-safety scan | plugin manifest file | no |
|
||||
| `plugins.code_safety.scan_failed` | warn | Plugin code scan could not complete (`--deep` only) | plugin path / scan environment | no |
|
||||
| `plugins.<pluginId>.security_audit_failed` | warn | A plugin-owned security audit collector threw an error | that plugin's security-audit collector | no |
|
||||
| `skills.code_safety` | warn/critical | Skill installer metadata/code contains suspicious or dangerous patterns (`--deep` only) | skill install source | no |
|
||||
| `skills.code_safety.scan_failed` | warn | Skill code scan could not complete (`--deep` only) | skill scan environment | no |
|
||||
| `security.exposure.open_channels_with_exec` | warn/critical | Shared/public rooms can reach exec-enabled agents | `channels.*.dmPolicy`, `channels.*.groupPolicy`, `tools.exec.*`, `agents.list[].tools.exec.*` | no |
|
||||
| `security.exposure.open_groups_with_elevated` | critical | Open DMs/groups + elevated tools create high-impact prompt-injection paths | top-level or nested DM policy paths, account overrides, `channels.*.groupPolicy` | no |
|
||||
| `security.exposure.open_groups_with_runtime_or_fs` | critical/warn | Open DMs/groups can reach command/file tools without sandbox/workspace guards | DM/group policy paths, `tools.profile/deny`, `tools.fs.workspaceOnly`, `agents.*.sandbox.mode` | no |
|
||||
| `security.exposure.open_groups_with_control_plane_tools` | critical | Open DMs/groups can reach gateway/cron control-plane tools | DM/group policy paths, `tools.allow`, `tools.alsoAllow`, `tools.profile`, `gateway`, `cron` | no |
|
||||
| `security.trust_model.multi_user_heuristic` | warn | Config looks multi-user while gateway trust model is personal-assistant | split trust boundaries, or shared-user hardening (`sandbox.mode`, tool deny/workspace scoping) | no |
|
||||
| `tools.profile_minimal_overridden` | warn | Agent overrides bypass global minimal profile | `agents.list[].tools.profile` | no |
|
||||
| `plugins.tools_reachable_permissive_policy` | warn | Extension tools reachable in permissive contexts | `tools.profile` + tool allow/deny | no |
|
||||
| `models.legacy` | warn | Legacy model families are still configured | model selection | no |
|
||||
| `models.weak_tier` | warn | Configured models are below current recommended tiers | model selection | no |
|
||||
| `models.small_params` | critical/info | Small models + unsafe tool surfaces raise injection risk | model choice + sandbox/tool policy | no |
|
||||
| `channels.<provider>.dm.open` | critical | `<provider>` DM policy is `"open"`; anyone can DM the bot | `channels.<provider>.dmPolicy`, `.allowFrom` | no |
|
||||
| `channels.<provider>.dm.open_invalid` | warn | `dmPolicy="open"` without `"*"` in `allowFrom` is inconsistent | `channels.<provider>.allowFrom` | no |
|
||||
| `channels.<provider>.dm.scope_main_multiuser` | warn | Multiple DM senders currently share the main session | `session.dmScope` | no |
|
||||
| `channels.<provider>.allowFrom.dangerous_name_matching_enabled` | info | `dangerouslyAllowNameMatching` re-enables mutable name/email/tag sender matching | disable `dangerouslyAllowNameMatching`, use stable sender IDs | no |
|
||||
| `channels.<provider>.account.read_only_resolution` | warn | A channel account could not be fully resolved for audit (missing secret/gateway) | ensure referenced secrets are resolvable, or run against a live gateway snapshot | no |
|
||||
| `channels.<provider>.warning.<n>` | info/warn/critical | Provider-specific security warning, classified from free-form plugin text | see finding detail | no |
|
||||
| `summary.attack_surface` | info | Roll-up summary of auth, channel, tool, and exposure posture | multiple keys (see finding detail) | no |
|
||||
| `checkId` | Severity | Why it matters | Primary fix key/path | Auto-fix |
|
||||
| --------------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------- |
|
||||
| `fs.state_dir.perms_world_writable` | critical | Other users/processes can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
|
||||
| `fs.state_dir.perms_group_writable` | warn | Group users can modify full OpenClaw state | filesystem perms on `~/.openclaw` | yes |
|
||||
| `fs.state_dir.perms_readable` | warn | State dir is readable by others | filesystem perms on `~/.openclaw` | yes |
|
||||
| `fs.state_dir.symlink` | warn | State dir target becomes another trust boundary | state dir filesystem layout | no |
|
||||
| `fs.config.perms_writable` | critical | Others can change auth/tool policy/config | filesystem perms on `~/.openclaw/openclaw.json` | yes |
|
||||
| `fs.config.symlink` | warn | Symlinked config files are unsupported for writes and add another trust boundary | replace with a regular config file or point `OPENCLAW_CONFIG_PATH` at the real file | no |
|
||||
| `fs.config.perms_group_readable` | warn | Group users can read config tokens/settings | filesystem perms on config file | yes |
|
||||
| `fs.config.perms_world_readable` | critical | Config can expose tokens/settings | filesystem perms on config file | yes |
|
||||
| `fs.config_include.perms_writable` | critical | Config include file can be modified by others | include-file perms referenced from `openclaw.json` | yes |
|
||||
| `fs.config_include.perms_group_readable` | warn | Group users can read included secrets/settings | include-file perms referenced from `openclaw.json` | yes |
|
||||
| `fs.config_include.perms_world_readable` | critical | Included secrets/settings are world-readable | include-file perms referenced from `openclaw.json` | yes |
|
||||
| `fs.auth_profiles.perms_writable` | critical | Others can inject or replace stored model credentials | `agents/<agentId>/agent/auth-profiles.json` perms | yes |
|
||||
| `fs.auth_profiles.perms_readable` | warn | Others can read API keys and OAuth tokens | `agents/<agentId>/agent/auth-profiles.json` perms | yes |
|
||||
| `fs.credentials_dir.perms_writable` | critical | Others can modify channel pairing/credential state | filesystem perms on `~/.openclaw/credentials` | yes |
|
||||
| `fs.credentials_dir.perms_readable` | warn | Others can read channel credential state | filesystem perms on `~/.openclaw/credentials` | yes |
|
||||
| `fs.sessions_store.perms_readable` | warn | Others can read session transcripts/metadata | session store perms | yes |
|
||||
| `fs.log_file.perms_readable` | warn | Others can read redacted-but-still-sensitive logs | gateway log file perms | yes |
|
||||
| `fs.synced_dir` | warn | State/config in iCloud/Dropbox/Drive broadens token/transcript exposure | move config/state off synced folders | no |
|
||||
| `gateway.bind_no_auth` | critical | Remote bind without shared secret | `gateway.bind`, `gateway.auth.*` | no |
|
||||
| `gateway.loopback_no_auth` | critical | Reverse-proxied loopback may become unauthenticated | `gateway.auth.*`, proxy setup | no |
|
||||
| `gateway.trusted_proxies_missing` | warn | Reverse-proxy headers are present but not trusted | `gateway.trustedProxies` | no |
|
||||
| `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.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 |
|
||||
| `gateway.control_ui.allowed_origins_wildcard` | warn/critical | `allowedOrigins=["*"]` disables browser-origin allowlisting | `gateway.controlUi.allowedOrigins` | no |
|
||||
| `gateway.control_ui.host_header_origin_fallback` | warn/critical | Enables Host-header origin fallback (DNS rebinding hardening downgrade) | `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback` | no |
|
||||
| `gateway.control_ui.insecure_auth` | warn | Insecure-auth compatibility toggle enabled | `gateway.controlUi.allowInsecureAuth` | no |
|
||||
| `gateway.control_ui.device_auth_disabled` | critical | Disables device identity check | `gateway.controlUi.dangerouslyDisableDeviceAuth` | no |
|
||||
| `gateway.real_ip_fallback_enabled` | warn/critical | Trusting `X-Real-IP` fallback can enable source-IP spoofing via proxy misconfig | `gateway.allowRealIpFallback`, `gateway.trustedProxies` | no |
|
||||
| `gateway.token_too_short` | warn | Short shared token is easier to brute force | `gateway.auth.token` | no |
|
||||
| `gateway.auth_no_rate_limit` | warn | Exposed auth without rate limiting increases brute-force risk | `gateway.auth.rateLimit` | no |
|
||||
| `gateway.trusted_proxy_auth` | critical | Proxy identity now becomes the auth boundary | `gateway.auth.mode="trusted-proxy"` | no |
|
||||
| `gateway.trusted_proxy_no_proxies` | critical | Trusted-proxy auth without trusted proxy IPs is unsafe | `gateway.trustedProxies` | no |
|
||||
| `gateway.trusted_proxy_no_user_header` | critical | Trusted-proxy auth cannot resolve user identity safely | `gateway.auth.trustedProxy.userHeader` | no |
|
||||
| `gateway.trusted_proxy_no_allowlist` | warn | Trusted-proxy auth accepts any authenticated upstream user | `gateway.auth.trustedProxy.allowUsers` | no |
|
||||
| `gateway.trusted_proxy_allow_loopback` | warn | Trusted-proxy auth accepts explicitly allowed loopback proxy sources | `gateway.auth.trustedProxy.allowLoopback` | no |
|
||||
| `gateway.probe_auth_secretref_unavailable` | warn | Deep probe could not resolve auth SecretRefs in this command path | deep-probe auth source / SecretRef availability | no |
|
||||
| `gateway.probe_failed` | warn | Live Gateway probe failed (`--deep` only) | gateway reachability/auth | no |
|
||||
| `discovery.mdns_full_mode` | warn/critical | mDNS full mode advertises `cliPath`/`sshPort` metadata on local network | `discovery.mdns.mode`, `gateway.bind` | no |
|
||||
| `config.insecure_or_dangerous_flags` | warn | One insecure/dangerous debug flag is enabled | key named in finding detail | no |
|
||||
| `security.audit.suppressions.active` | info | Audit output has configured suppressions and may be filtered | `security.audit.suppressions` | no |
|
||||
| `config.secrets.gateway_password_in_config` | warn | Gateway password is stored directly in config | `gateway.auth.password` | no |
|
||||
| `config.secrets.hooks_token_in_config` | warn | Hook bearer token is stored directly in config | `hooks.token` | no |
|
||||
| `hooks.token_reuse_gateway_token` | critical | Hook ingress token also unlocks Gateway auth | `hooks.token`, `gateway.auth.token`, `gateway.auth.password` | no |
|
||||
| `hooks.token_too_short` | warn | Easier brute force on hook ingress | `hooks.token` | no |
|
||||
| `hooks.default_session_key_unset` | warn | Hook agent runs fan out into generated per-request sessions | `hooks.defaultSessionKey` | no |
|
||||
| `hooks.allowed_agent_ids_unrestricted` | warn/critical | Authenticated hook callers may route to any configured agent | `hooks.allowedAgentIds` | no |
|
||||
| `hooks.request_session_key_enabled` | warn/critical | External caller can choose sessionKey | `hooks.allowRequestSessionKey` | no |
|
||||
| `hooks.request_session_key_prefixes_missing` | warn/critical | No bound on external session key shapes | `hooks.allowedSessionKeyPrefixes` | no |
|
||||
| `hooks.path_root` | critical | Hook path is `/`, making ingress easier to collide or misroute | `hooks.path` | no |
|
||||
| `hooks.installs_unpinned_npm_specs` | warn | Hook install records are not pinned to immutable npm specs | hook install metadata | no |
|
||||
| `hooks.installs_missing_integrity` | warn | Hook install records lack integrity metadata | hook install metadata | no |
|
||||
| `hooks.installs_version_drift` | warn | Hook install records drift from installed packages | hook install metadata | no |
|
||||
| `logging.redact_off` | warn | Sensitive values leak to logs/status | `logging.redactSensitive` | yes |
|
||||
| `browser.control_invalid_config` | warn | Browser control config is invalid before runtime | `browser.*` | no |
|
||||
| `browser.control_no_auth` | critical | Browser control exposed without token/password auth | `gateway.auth.*` | no |
|
||||
| `browser.remote_cdp_http` | warn | Remote CDP over plain HTTP lacks transport encryption | browser profile `cdpUrl` | no |
|
||||
| `browser.remote_cdp_private_host` | warn | Remote CDP targets a private/internal host | browser profile `cdpUrl`, `browser.ssrfPolicy.*` | no |
|
||||
| `sandbox.docker_config_mode_off` | warn | Sandbox Docker config present but inactive | `agents.*.sandbox.mode` | no |
|
||||
| `sandbox.bind_mount_non_absolute` | warn | Relative bind mounts can resolve unpredictably | `agents.*.sandbox.docker.binds[]` | no |
|
||||
| `sandbox.dangerous_bind_mount` | critical | Sandbox bind mount targets blocked system, credential, or Docker socket paths | `agents.*.sandbox.docker.binds[]` | no |
|
||||
| `sandbox.dangerous_network_mode` | critical | Sandbox Docker network uses `host` or `container:*` namespace-join mode | `agents.*.sandbox.docker.network` | no |
|
||||
| `sandbox.dangerous_seccomp_profile` | critical | Sandbox seccomp profile weakens container isolation | `agents.*.sandbox.docker.securityOpt` | no |
|
||||
| `sandbox.dangerous_apparmor_profile` | critical | Sandbox AppArmor profile weakens container isolation | `agents.*.sandbox.docker.securityOpt` | no |
|
||||
| `sandbox.browser_cdp_bridge_unrestricted` | warn | Sandbox browser bridge is exposed without source-range restriction | `sandbox.browser.cdpSourceRange` | no |
|
||||
| `sandbox.browser_container.non_loopback_publish` | critical | Existing browser container publishes CDP on non-loopback interfaces | browser sandbox container publish config | no |
|
||||
| `sandbox.browser_container.hash_label_missing` | warn | Existing browser container predates current config-hash labels | `openclaw sandbox recreate --browser --all` | no |
|
||||
| `sandbox.browser_container.hash_epoch_stale` | warn | Existing browser container predates current browser config epoch | `openclaw sandbox recreate --browser --all` | no |
|
||||
| `sandbox.browser_container.docker_probe_timeout` | warn | Docker label probe for the browser container timed out | Docker daemon reachability | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` fails closed when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` fails closed when sandbox is off | `agents.entries.*.tools.exec.host`, `agents.entries.*.sandbox.mode` | no |
|
||||
| `tools.exec.security_full_configured` | warn/critical | Host exec is running with `security="full"` | `tools.exec.security`, `agents.entries.*.tools.exec.security` | no |
|
||||
| `tools.exec.agent_skill_mcp_boundary_drift` | warn | Agent skill allowlists are present while host exec can reach MCP clients/registries | `agents.entries.*.tools.exec.*`, sandbox/OS isolation, MCP server credentials | no |
|
||||
| `tools.exec.fs_tools_disabled_but_exec_enabled` | warn | Filesystem tool policy does not make shell execution read-only | `tools.deny`, `agents.entries.*.tools.deny`, `agents.*.sandbox.workspaceAccess` | no |
|
||||
| `tools.exec.auto_allow_skills_enabled` | warn | Exec approvals trust skill bins implicitly | host approvals file | no |
|
||||
| `tools.exec.allowlist_interpreter_without_strict_inline_eval` | warn | Interpreter allowlists permit inline eval without forced reapproval | `tools.exec.strictInlineEval`, `agents.entries.*.tools.exec.strictInlineEval`, exec approvals allowlist | no |
|
||||
| `tools.exec.safe_bins_interpreter_unprofiled` | warn | Interpreter/runtime bins in `safeBins` without explicit profiles broaden exec risk | `tools.exec.safeBins`, `tools.exec.safeBinProfiles`, `agents.entries.*.tools.exec.*` | no |
|
||||
| `tools.exec.safe_bins_broad_behavior` | warn | Broad-behavior tools in `safeBins` weaken the low-risk stdin-filter trust model | `tools.exec.safeBins`, `agents.entries.*.tools.exec.safeBins` | no |
|
||||
| `tools.exec.safe_bin_trusted_dirs_risky` | warn | `safeBinTrustedDirs` includes mutable or risky directories | `tools.exec.safeBinTrustedDirs`, `agents.entries.*.tools.exec.safeBinTrustedDirs` | no |
|
||||
| `tools.elevated.allowFrom.<provider>.wildcard` | critical | `tools.elevated.allowFrom.<provider>` includes `"*"`, approving every sender | `tools.elevated.allowFrom.<provider>` | no |
|
||||
| `tools.elevated.allowFrom.<provider>.large` | warn | Elevated allowlist for `<provider>` has more than 25 entries | `tools.elevated.allowFrom.<provider>` | no |
|
||||
| `agents.claude_cli.permission_mode_overridden_by_yolo` | warn | Claude CLI `--permission-mode` is ignored because OpenClaw exec is fully unattended | `tools.exec.security`, `tools.exec.ask`, `cliBackends.claude-cli` args | no |
|
||||
| `skills.workspace.symlink_escape` | warn | Workspace `skills/**/SKILL.md` resolves outside workspace root (symlink-chain drift) | workspace `skills/**` filesystem state | no |
|
||||
| `skills.workspace.scan_truncated` | warn | Workspace skill scan hit its directory-visit cap before finishing | flatten/simplify the workspace `skills/` directory tree | no |
|
||||
| `plugins.extensions_no_allowlist` | warn | Plugins are installed without an explicit plugin allowlist | `plugins.allowlist` | no |
|
||||
| `plugins.allow_phantom_entries` | warn | `plugins.allow` lists an ID with no matching installed plugin | `plugins.allow` | no |
|
||||
| `plugins.installs_unpinned_npm_specs` | warn | Plugin index records are not pinned to immutable npm specs | plugin install metadata | no |
|
||||
| `plugins.installs_missing_integrity` | warn | Plugin index records lack integrity metadata | plugin install metadata | no |
|
||||
| `plugins.installs_version_drift` | warn | Plugin index records drift from installed packages | plugin install metadata | no |
|
||||
| `plugins.code_safety` | warn/critical | Plugin code scan found suspicious or dangerous patterns (`--deep` only) | plugin code / install source | no |
|
||||
| `plugins.code_safety.entry_path` | warn | Plugin entry path points into hidden or `node_modules` locations | plugin manifest `entry` | no |
|
||||
| `plugins.code_safety.entry_escape` | critical | Plugin entry escapes the plugin directory | plugin manifest `entry` | no |
|
||||
| `plugins.code_safety.manifest_parse_error` | warn | Plugin manifest could not be parsed during the code-safety scan | plugin manifest file | no |
|
||||
| `plugins.code_safety.scan_failed` | warn | Plugin code scan could not complete (`--deep` only) | plugin path / scan environment | no |
|
||||
| `plugins.<pluginId>.security_audit_failed` | warn | A plugin-owned security audit collector threw an error | that plugin's security-audit collector | no |
|
||||
| `skills.code_safety` | warn/critical | Skill installer metadata/code contains suspicious or dangerous patterns (`--deep` only) | skill install source | no |
|
||||
| `skills.code_safety.scan_failed` | warn | Skill code scan could not complete (`--deep` only) | skill scan environment | no |
|
||||
| `security.exposure.open_channels_with_exec` | warn/critical | Shared/public rooms can reach exec-enabled agents | `channels.*.dmPolicy`, `channels.*.groupPolicy`, `tools.exec.*`, `agents.entries.*.tools.exec.*` | no |
|
||||
| `security.exposure.open_groups_with_elevated` | critical | Open DMs/groups + elevated tools create high-impact prompt-injection paths | top-level or nested DM policy paths, account overrides, `channels.*.groupPolicy` | no |
|
||||
| `security.exposure.open_groups_with_runtime_or_fs` | critical/warn | Open DMs/groups can reach command/file tools without sandbox/workspace guards | DM/group policy paths, `tools.profile/deny`, `tools.fs.workspaceOnly`, `agents.*.sandbox.mode` | no |
|
||||
| `security.exposure.open_groups_with_control_plane_tools` | critical | Open DMs/groups can reach gateway/cron control-plane tools | DM/group policy paths, `tools.allow`, `tools.alsoAllow`, `tools.profile`, `gateway`, `cron` | no |
|
||||
| `security.trust_model.multi_user_heuristic` | warn | Config looks multi-user while gateway trust model is personal-assistant | split trust boundaries, or shared-user hardening (`sandbox.mode`, tool deny/workspace scoping) | no |
|
||||
| `tools.profile_minimal_overridden` | warn | Agent overrides bypass global minimal profile | `agents.entries.*.tools.profile` | no |
|
||||
| `plugins.tools_reachable_permissive_policy` | warn | Extension tools reachable in permissive contexts | `tools.profile` + tool allow/deny | no |
|
||||
| `models.legacy` | warn | Legacy model families are still configured | model selection | no |
|
||||
| `models.weak_tier` | warn | Configured models are below current recommended tiers | model selection | no |
|
||||
| `models.small_params` | critical/info | Small models + unsafe tool surfaces raise injection risk | model choice + sandbox/tool policy | no |
|
||||
| `channels.<provider>.dm.open` | critical | `<provider>` DM policy is `"open"`; anyone can DM the bot | `channels.<provider>.dmPolicy`, `.allowFrom` | no |
|
||||
| `channels.<provider>.dm.open_invalid` | warn | `dmPolicy="open"` without `"*"` in `allowFrom` is inconsistent | `channels.<provider>.allowFrom` | no |
|
||||
| `channels.<provider>.dm.scope_main_multiuser` | warn | Multiple DM senders currently share the main session | `session.dmScope` | no |
|
||||
| `channels.<provider>.allowFrom.dangerous_name_matching_enabled` | info | `dangerouslyAllowNameMatching` re-enables mutable name/email/tag sender matching | disable `dangerouslyAllowNameMatching`, use stable sender IDs | no |
|
||||
| `channels.<provider>.account.read_only_resolution` | warn | A channel account could not be fully resolved for audit (missing secret/gateway) | ensure referenced secrets are resolvable, or run against a live gateway snapshot | no |
|
||||
| `channels.<provider>.warning.<n>` | info/warn/critical | Provider-specific security warning, classified from free-form plugin text | see finding detail | no |
|
||||
| `summary.attack_surface` | info | Roll-up summary of auth, channel, tool, and exposure posture | multiple keys (see finding detail) | no |
|
||||
|
||||
`channels.<provider>.*` and `tools.elevated.allowFrom.<provider>.*` checkIds are
|
||||
generated per configured channel/provider, so `<provider>` is a real channel id
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -173,7 +173,7 @@ Treat `dmPolicy="open"` and `groupPolicy="open"` as last-resort settings; prefer
|
||||
|
||||
- **DM allowlist** (`allowFrom` / `channels.discord.allowFrom` / `channels.slack.allowFrom`; legacy: `channels.discord.dm.allowFrom`, `channels.slack.dm.allowFrom`): who can DM the bot. When `dmPolicy="pairing"`, approvals write to `~/.openclaw/credentials/<channel>-allowFrom.json` (default account) or `<channel>-<accountId>-allowFrom.json` (non-default accounts), merged with config allowlists.
|
||||
- **Group allowlist** (channel-specific): which groups/channels/guilds the bot accepts at all.
|
||||
- `channels.whatsapp.groups`, `channels.telegram.groups`, `channels.imessage.groups`: per-group defaults like `requireMention`; when set, also acts as a group allowlist (include `"*"` to keep allow-all behavior). Customize mention triggers with `agents.list[].groupChat.mentionPatterns` (for example `["@openclaw", "@mybot"]`) so `requireMention` gates on your own bot names.
|
||||
- `channels.whatsapp.groups`, `channels.telegram.groups`, `channels.imessage.groups`: per-group defaults like `requireMention`; when set, also acts as a group allowlist (include `"*"` to keep allow-all behavior). Customize mention triggers with `agents.entries.*.groupChat.mentionPatterns` (for example `["@openclaw", "@mybot"]`) so `requireMention` gates on your own bot names.
|
||||
- `groupPolicy="allowlist"` + `groupAllowFrom`: restrict who can trigger the bot inside a group session (WhatsApp/Telegram/Signal/iMessage/Microsoft Teams).
|
||||
- `channels.discord.guilds` / `channels.slack.channels`: per-surface allowlists + mention defaults.
|
||||
- Check order: `groupPolicy`/group allowlists first, then mention/reply activation. Replying to a bot message (implicit mention) does **not** bypass `groupAllowFrom`.
|
||||
@@ -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` / `gateway.nodes.commands.deny`. The deny list 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.
|
||||
@@ -360,7 +360,7 @@ Agent workspace access inside the sandbox (`agents.defaults.sandbox.workspaceAcc
|
||||
Extra `sandbox.docker.binds` are validated against normalized, canonicalized source paths. A blocked-path denylist covers `/etc`, `/private/etc`, `/proc`, `/sys`, `/dev`, `/root`, `/boot`, and directories that commonly contain or alias the Docker socket (`/run`, `/var/run`, and `docker.sock` under them), plus HOME credential subpaths (`.aws`, `.cargo`, `.config`, `.docker`, `.gnupg`, `.netrc`, `.npm`, `.ssh`). Parent-symlink tricks and canonical home aliases are resolved through existing ancestors and re-checked, so they still fail closed if they resolve into a blocked root.
|
||||
|
||||
<Warning>
|
||||
`tools.elevated` is the global baseline escape hatch that runs exec outside the sandbox. The effective host is `gateway` by default, or `node` when the exec target is configured to `node`. Keep `tools.elevated.allowFrom` tight and do not enable it for strangers. Further restrict per agent via `agents.list[].tools.elevated`. See [Elevated mode](/tools/elevated).
|
||||
`tools.elevated` is the global baseline escape hatch that runs exec outside the sandbox. The effective host is `gateway` by default, or `node` when the exec target is configured to `node`. Keep `tools.elevated.allowFrom` tight and do not enable it for strangers. Further restrict per agent via `agents.entries.*.tools.elevated`. See [Elevated mode](/tools/elevated).
|
||||
</Warning>
|
||||
|
||||
### Sub-agent delegation guardrail
|
||||
@@ -368,7 +368,7 @@ Extra `sandbox.docker.binds` are validated against normalized, canonicalized sou
|
||||
If you allow session tools, treat delegated sub-agent runs as another boundary decision:
|
||||
|
||||
- Deny `sessions_spawn` unless the agent truly needs delegation.
|
||||
- Keep `agents.defaults.subagents.allowAgents` and any per-agent `agents.list[].subagents.allowAgents` overrides restricted to known-safe target agents.
|
||||
- Keep `agents.defaults.subagents.allowAgents` and any per-agent `agents.entries.*.subagents.allowAgents` overrides restricted to known-safe target agents.
|
||||
- For workflows that must remain sandboxed, call `sessions_spawn` with `sandbox: "require"` (default is `"inherit"`); `"require"` fails fast when the target child runtime is not sandboxed.
|
||||
|
||||
### Read-only mode
|
||||
|
||||
@@ -15,8 +15,8 @@ Debugging helpers for streaming output, gateway iteration, and startup profiling
|
||||
|
||||
```text
|
||||
/debug show
|
||||
/debug set messages.responsePrefix="[openclaw]"
|
||||
/debug unset messages.responsePrefix
|
||||
/debug set channels.whatsapp.responsePrefix="[openclaw]"
|
||||
/debug unset channels.whatsapp.responsePrefix
|
||||
/debug reset
|
||||
```
|
||||
|
||||
|
||||
@@ -22,6 +22,68 @@ On fresh Ubuntu installs that use the default state dir, OpenClaw also treats `~
|
||||
|
||||
If the config file is missing entirely, step 4 is skipped; shell import still runs if enabled.
|
||||
|
||||
## Supported operator-facing variables
|
||||
|
||||
The variables below are the supported environment contract for operators. Undocumented `OPENCLAW_*` variables are internal implementation details and may disappear without notice.
|
||||
|
||||
### Paths and instances
|
||||
|
||||
| Variable | Purpose |
|
||||
| ------------------------ | ----------------------------------------------------------------- |
|
||||
| `OPENCLAW_HOME` | Override the home directory used for OpenClaw path defaults. |
|
||||
| `OPENCLAW_STATE_DIR` | Override the mutable state directory. |
|
||||
| `OPENCLAW_CONFIG_PATH` | Override the active config file path. |
|
||||
| `OPENCLAW_WORKSPACE_DIR` | Override the default agent workspace. |
|
||||
| `OPENCLAW_PROFILE` | Select a named profile and its isolated defaults. |
|
||||
| `OPENCLAW_GIT_DIR` | Override the source checkout used by development-channel updates. |
|
||||
| `OPENCLAW_INCLUDE_ROOTS` | Allow `$include` to resolve from additional roots. |
|
||||
|
||||
### Gateway and authentication
|
||||
|
||||
| Variable | Purpose |
|
||||
| --------------------------- | --------------------------------------------------------------- |
|
||||
| `OPENCLAW_GATEWAY_URL` | Override the remote Gateway URL used by clients. |
|
||||
| `OPENCLAW_GATEWAY_PORT` | Override the local Gateway port. |
|
||||
| `OPENCLAW_GATEWAY_TOKEN` | Supply token authentication for Gateway servers and clients. |
|
||||
| `OPENCLAW_GATEWAY_PASSWORD` | Supply password authentication for Gateway servers and clients. |
|
||||
|
||||
### Provider credentials
|
||||
|
||||
Core and bundled provider plugins recognize the following credential and provider-selection variables. Prefer each provider's config or SecretRef fields when you need scoped credentials rather than one process-wide value.
|
||||
|
||||
`AI_GATEWAY_API_KEY`, `ANTHROPIC_ADMIN_API_KEY`, `ANTHROPIC_ADMIN_KEY`, `ANTHROPIC_API_KEY`, `ANTHROPIC_OAUTH_TOKEN`, `ARCEEAI_API_KEY`, `AZURE_OPENAI_API_KEY`, `AZURE_SPEECH_API_KEY`, `AZURE_SPEECH_KEY`, `AZURE_SPEECH_REGION`, `BASETEN_API_KEY`, `BRAVE_API_KEY`, `BYTEPLUS_API_KEY`, `BYTEPLUS_SEED_SPEECH_API_KEY`, `CEREBRAS_API_KEY`, `CHUTES_API_KEY`, `CHUTES_OAUTH_TOKEN`, `CLAWROUTER_API_KEY`, `CLOUDFLARE_AI_GATEWAY_API_KEY`, `CODEX_API_KEY`, `COHERE_API_KEY`, `COMFY_API_KEY`, `COMFY_CLOUD_API_KEY`, `COPILOT_GITHUB_TOKEN`, `DASHSCOPE_API_KEY`, `DEEPGRAM_API_KEY`, `DEEPINFRA_API_KEY`, `DEEPSEEK_API_KEY`, `ELEVENLABS_API_KEY`, `EXA_API_KEY`, `FAL_API_KEY`, `FAL_KEY`, `FEATHERLESS_API_KEY`, `FIRECRAWL_API_KEY`, `FIREWORKS_API_KEY`, `GCLOUD_PROJECT`, `GEMINI_API_KEY`, `GH_TOKEN`, `GITHUB_TOKEN`, `GMI_API_KEY`, `GOOGLE_API_KEY`, `GOOGLE_APPLICATION_CREDENTIALS`, `GOOGLE_CLOUD_API_KEY`, `GOOGLE_CLOUD_LOCATION`, `GOOGLE_CLOUD_PROJECT`, `GRADIUM_API_KEY`, `GROQ_API_KEY`, `HF_TOKEN`, `HUGGINGFACE_HUB_TOKEN`, `INWORLD_API_KEY`, `KILOCODE_API_KEY`, `KIMICODE_API_KEY`, `KIMI_API_KEY`, `LITELLM_API_KEY`, `LM_API_TOKEN`, `LONGCAT_API_KEY`, `MINIMAX_API_KEY`, `MINIMAX_CODE_PLAN_KEY`, `MINIMAX_CODING_API_KEY`, `MINIMAX_OAUTH_TOKEN`, `MISTRAL_API_KEY`, `MODELSTUDIO_API_KEY`, `MODEL_API_KEY`, `MOONSHOT_API_KEY`, `NOVITA_API_KEY`, `NVIDIA_API_KEY`, `OLLAMA_API_KEY`, `OPENAI_ADMIN_KEY`, `OPENAI_API_KEY`, `OPENCODE_API_KEY`, `OPENCODE_ZEN_API_KEY`, `OPENROUTER_API_KEY`, `PARALLEL_API_KEY`, `PERPLEXITY_API_KEY`, `PIXVERSE_API_KEY`, `QIANFAN_API_KEY`, `QWEN_API_KEY`, `QWEN_TOKEN_PLAN_API_KEY`, `RUNWAYML_API_SECRET`, `RUNWAY_API_KEY`, `SENSEAUDIO_API_KEY`, `SGLANG_API_KEY`, `SPEECH_KEY`, `SPEECH_REGION`, `STEPFUN_API_KEY`, `SYNTHETIC_API_KEY`, `TAVILY_API_KEY`, `TOGETHER_API_KEY`, `TOKENHUB_API_KEY`, `TOKENPLAN_API_KEY`, `VENICE_API_KEY`, `VLLM_API_KEY`, `VOLCANO_ENGINE_API_KEY`, `VOLCENGINE_TTS_API_KEY`, `VOLCENGINE_TTS_APPID`, `VOLCENGINE_TTS_TOKEN`, `VOYAGE_API_KEY`, `VYDRA_API_KEY`, `XAI_API_KEY`, `XIAOMI_API_KEY`, `XIAOMI_TOKEN_PLAN_API_KEY`, `XI_API_KEY`, `ZAI_API_KEY`, and `Z_AI_API_KEY`.
|
||||
|
||||
Installed third-party plugins may declare additional credential variables in their plugin manifests; those variables are contracts of the plugin that declares them, not core OpenClaw variables.
|
||||
|
||||
### Logging and diagnostics
|
||||
|
||||
| Variable | Purpose |
|
||||
| ------------------------------------ | ------------------------------------------------------------- |
|
||||
| `OPENCLAW_LOG_LEVEL` | Override file and console log levels. |
|
||||
| `OPENCLAW_DEBUG_MODEL_TRANSPORT` | Enable model transport timing diagnostics. |
|
||||
| `OPENCLAW_DEBUG_MODEL_PAYLOAD` | Select redacted model payload diagnostics. |
|
||||
| `OPENCLAW_DEBUG_SSE` | Select SSE timing or event-peek diagnostics. |
|
||||
| `OPENCLAW_DEBUG_CODE_MODE` | Enable code-mode surface diagnostics. |
|
||||
| `OPENCLAW_DIAGNOSTICS` | Enable named diagnostic flags, or disable all flags with `0`. |
|
||||
| `OPENCLAW_DIAGNOSTICS_TIMELINE_PATH` | Select the JSONL path for timeline diagnostics. |
|
||||
| `OPENCLAW_DIAGNOSTICS_EVENT_LOOP` | Add event-loop samples to timeline diagnostics. |
|
||||
|
||||
### Feature and runtime toggles
|
||||
|
||||
| Variable | Purpose |
|
||||
| ------------------------------------ | ---------------------------------------------------------------------------- |
|
||||
| `OPENCLAW_LOAD_SHELL_ENV` | Import missing expected variables from the login shell. |
|
||||
| `OPENCLAW_SHELL_ENV_TIMEOUT_MS` | Set the login-shell import timeout. |
|
||||
| `OPENCLAW_EXEC_SHELL_SNAPSHOT` | Disable exec shell snapshots with `0`. |
|
||||
| `OPENCLAW_OFFLINE` | Prevent downloads of pinned agent helper binaries. |
|
||||
| `OPENCLAW_BROWSER_HEADLESS` | Force managed browser launches headed (`0`) or headless (`1`). |
|
||||
| `OPENCLAW_DISABLE_BONJOUR` | Force Bonjour advertising on (`0`) or off (`1`). |
|
||||
| `OPENCLAW_NO_AUTO_UPDATE` | Disable automatic update applies. |
|
||||
| `OPENCLAW_ALLOW_INSECURE_PRIVATE_WS` | Allow trusted private-DNS `ws://` connections as a break-glass override. |
|
||||
| `OPENCLAW_ALLOW_MULTI_GATEWAY` | Allow multiple Gateway processes while preserving per-state ownership locks. |
|
||||
| `OPENCLAW_SKIP_CHANNELS` | Start the Gateway without channel transports for troubleshooting. |
|
||||
| `OPENCLAW_THEME` | Force the TUI palette to `light` or `dark`. |
|
||||
|
||||
## Provider credentials and workspace `.env`
|
||||
|
||||
Do not keep provider API keys only in a workspace `.env`. OpenClaw blocks a large set of provider credential and endpoint-redirect keys from workspace `.env` files, including every known provider auth env var (for example `GEMINI_API_KEY`, `GOOGLE_API_KEY`, `XAI_API_KEY`, `MISTRAL_API_KEY`, `GROQ_API_KEY`, `DEEPSEEK_API_KEY`, `PERPLEXITY_API_KEY`, `BRAVE_API_KEY`, `TAVILY_API_KEY`, `EXA_API_KEY`, `FIRECRAWL_API_KEY`), plus any key ending in `_API_HOST`, `_BASE_URL`, `_ENDPOINT`, or `_HOMESERVER`, and the entire `OPENCLAW_*`, `CLAWHUB_*`, `ANTHROPIC_API_KEY_*`, and `OPENAI_API_KEY_*` namespaces.
|
||||
|
||||
+10
-30
@@ -128,11 +128,11 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="How do I customize skills without keeping the repo dirty?">
|
||||
Use managed overrides instead of editing the repo copy. Put changes in `~/.openclaw/skills/<name>/SKILL.md` (or add a folder via `skills.load.extraDirs` in `~/.openclaw/openclaw.json`). Precedence: `<workspace>/skills` -> `<workspace>/.agents/skills` -> `~/.agents/skills` -> `~/.openclaw/skills` -> bundled -> `skills.load.extraDirs`, so managed overrides win over bundled skills without touching git. To install globally but limit visibility to some agents, keep the shared copy in `~/.openclaw/skills` and control visibility with `agents.defaults.skills` / `agents.list[].skills`. Only upstream-worthy edits should go out as PRs against the repo copy.
|
||||
Use managed overrides instead of editing the repo copy. Put changes in `~/.openclaw/skills/<name>/SKILL.md` (or add a folder via `skills.load.extraDirs` in `~/.openclaw/openclaw.json`). Precedence: `<workspace>/skills` -> `<workspace>/.agents/skills` -> `~/.agents/skills` -> `~/.openclaw/skills` -> bundled -> `skills.load.extraDirs`, so managed overrides win over bundled skills without touching git. To install globally but limit visibility to some agents, keep the shared copy in `~/.openclaw/skills` and control visibility with `agents.defaults.skills` / `agents.entries.*.skills`. Only upstream-worthy edits should go out as PRs against the repo copy.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I load skills from a custom folder?">
|
||||
Yes: add directories via `skills.load.extraDirs` in `~/.openclaw/openclaw.json` (lowest precedence in the order above). `clawhub` installs into `./skills` by default, which OpenClaw treats as `<workspace>/skills` on the next session. To limit visibility to certain agents, pair with `agents.defaults.skills` or `agents.list[].skills`.
|
||||
Yes: add directories via `skills.load.extraDirs` in `~/.openclaw/openclaw.json` (lowest precedence in the order above). `clawhub` installs into `./skills` by default, which OpenClaw treats as `<workspace>/skills` on the next session. To limit visibility to certain agents, pair with `agents.defaults.skills` or `agents.entries.*.skills`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How can I use different models or settings for different tasks?">
|
||||
@@ -165,7 +165,7 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
}
|
||||
```
|
||||
|
||||
Put shared per-model defaults in `agents.defaults.models["provider/model"].params`, then agent-specific overrides in flat `agents.list[].params`. Do not duplicate the same model under nested `agents.list[].models["provider/model"].params`; that path is for per-agent model catalog and runtime overrides.
|
||||
Put shared per-model defaults in `agents.defaults.models["provider/model"].params`, then agent-specific overrides in flat `agents.entries.*.params`. Do not duplicate the same model under nested `agents.entries.*.models["provider/model"].params`; that path is for per-agent model catalog and runtime overrides.
|
||||
|
||||
See [Cron jobs](/automation/cron-jobs), [Multi-Agent Routing](/concepts/multi-agent), [Configuration](/gateway/config-agents), [Slash commands](/tools/slash-commands).
|
||||
|
||||
@@ -189,7 +189,7 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
- `/session idle <duration|off>` and `/session max-age <duration|off>` control auto-unfocus.
|
||||
- `/unfocus` detaches the thread.
|
||||
|
||||
Config: `session.threadBindings.enabled` (global switch), `session.threadBindings.idleHours` (default `24`, `0` disables), `session.threadBindings.maxAgeHours` (default `0` = no hard cap), and per-channel overrides `channels.discord.threadBindings.{enabled,idleHours,maxAgeHours}`. `channels.discord.threadBindings.spawnSessions` gates auto-bind on spawn (default `true`).
|
||||
Config: `session.threadBindings.enabled` (global switch), `session.threadBindings.idleHours` (default `24`, `0` disables), `session.threadBindings.maxAgeHours` (default `0` = no hard cap), and `session.threadBindings.spawnSessions` for auto-bind on spawn (default `true`).
|
||||
|
||||
Docs: [Sub-agents](/tools/subagents), [Discord](/channels/discord), [Configuration Reference](/gateway/configuration-reference), [Slash commands](/tools/slash-commands).
|
||||
|
||||
@@ -279,7 +279,7 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
openclaw skills check
|
||||
```
|
||||
|
||||
Native `openclaw skills install` writes into the active workspace `skills/` directory by default. Add `--global` to install into the shared managed skills directory for all local agents. Install the separate `clawhub` CLI only to publish or sync your own skills. Use `agents.defaults.skills` or `agents.list[].skills` to narrow which agents see shared skills.
|
||||
Native `openclaw skills install` writes into the active workspace `skills/` directory by default. Add `--global` to install into the shared managed skills directory for all local agents. Install the separate `clawhub` CLI only to publish or sync your own skills. Use `agents.defaults.skills` or `agents.entries.*.skills` to narrow which agents see shared skills.
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -339,7 +339,7 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
openclaw skills update --all
|
||||
```
|
||||
|
||||
Native installs land in the active workspace `skills/` directory; use `--global` for all local agents, or configure `agents.defaults.skills` / `agents.list[].skills` to limit visibility. Some skills expect Homebrew-installed binaries; on Linux that means Linuxbrew.
|
||||
Native installs land in the active workspace `skills/` directory; use `--global` for all local agents, or configure `agents.defaults.skills` / `agents.entries.*.skills` to limit visibility. Some skills expect Homebrew-installed binaries; on Linux that means Linuxbrew.
|
||||
|
||||
See [Skills](/tools/skills), [Skills config](/tools/skills-config), [ClawHub](/tools/clawhub).
|
||||
|
||||
@@ -432,7 +432,7 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
<Accordion title="Does semantic memory search require an OpenAI API key?">
|
||||
Only if you use **OpenAI embeddings**, which is the default provider. Codex OAuth covers chat/completions and does **not** grant embeddings access, so signing in with Codex (OAuth or the Codex CLI login) does not enable semantic memory search. OpenAI embeddings still need a real API key (`OPENAI_API_KEY` or `models.providers.openai.apiKey`).
|
||||
|
||||
To stay local, set `agents.defaults.memorySearch.provider: "local"` (GGUF/llama.cpp). Other supported providers: Bedrock, DeepInfra, Gemini (`GEMINI_API_KEY` or `memorySearch.remote.apiKey`), GitHub Copilot, LM Studio, Mistral, Ollama, OpenAI-compatible, and Voyage. See [Memory](/concepts/memory) and [Memory search](/concepts/memory-search) for setup details.
|
||||
To stay local, set `memory.search.provider: "local"` (GGUF/llama.cpp). Other supported providers: Bedrock, DeepInfra, Gemini (`GEMINI_API_KEY` or `memory.search.remote.apiKey`), GitHub Copilot, LM Studio, Mistral, Ollama, OpenAI-compatible, and Voyage. See [Memory](/concepts/memory) and [Memory search](/concepts/memory-search) for setup details.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -510,7 +510,7 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
}
|
||||
```
|
||||
|
||||
Or override one agent under `agents.list[].bootstrapMaxChars` / `bootstrapTotalMaxChars`.
|
||||
Or override one agent under `agents.entries.*.bootstrapMaxChars` / `bootstrapTotalMaxChars`.
|
||||
|
||||
Use `/context` to check raw vs injected sizes and whether truncation happened. Keep `SOUL.md` focused on voice, stance, and personality; put operating rules in `AGENTS.md` and durable facts in memory.
|
||||
|
||||
@@ -592,26 +592,6 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
The Gateway watches the config and supports hot-reload: `gateway.reload.mode: "hybrid"` (default) hot-applies safe changes and restarts for critical ones. `hot`, `restart`, and `off` are also supported. Most `tools.*`, `agents.*` policy, `session.*`, and `messages.*` changes apply immediately with no reload action at all; `gateway.*` binding/port changes require a restart.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I disable funny CLI taglines?">
|
||||
Set `cli.banner.taglineMode`:
|
||||
|
||||
```json5
|
||||
{
|
||||
cli: {
|
||||
banner: {
|
||||
taglineMode: "off", // random | default | off
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `off`: hides tagline text but keeps the banner title/version line.
|
||||
- `default`: always uses `All your chats, one OpenClaw.`.
|
||||
- `random`: rotating funny/seasonal taglines (default behavior).
|
||||
- For no banner at all, set env `OPENCLAW_HIDE_BANNER=1`.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I enable web search (and web fetch)?">
|
||||
`web_fetch` works without an API key. `web_search` depends on your selected provider:
|
||||
|
||||
@@ -977,7 +957,7 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
}
|
||||
```
|
||||
|
||||
`resetByType` supports `direct` (legacy alias `dm`), `group`, and `thread`. Legacy top-level `session.idleMinutes` still works as a compatibility alias for an idle-mode default when no `session.reset`/`resetByType` block is set. See [Session management](/concepts/session) for the full lifecycle.
|
||||
`resetByType` supports `direct`, `group`, and `thread`. Doctor migrates legacy `dm` entries to `direct`; the schema rejects `dm`. Legacy top-level `session.idleMinutes` still works as a compatibility alias for an idle-mode default when no `session.reset`/`resetByType` block is set. See [Session management](/concepts/session) for the full lifecycle.
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -1056,7 +1036,7 @@ First-run Q&A - install, onboard, auth routes, subscriptions, initial failures -
|
||||
|
||||
If `HEARTBEAT.md` exists but is effectively empty (only blank lines, Markdown/HTML comments, ATX headings, fence markers, or empty list-item stubs), OpenClaw skips the heartbeat run to save API calls. If the file is missing, the heartbeat still runs and the model decides what to do.
|
||||
|
||||
Per-agent overrides use `agents.list[].heartbeat`. Docs: [Heartbeat](/gateway/heartbeat).
|
||||
Per-agent overrides use `agents.entries.*.heartbeat`. Docs: [Heartbeat](/gateway/heartbeat).
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ Common causes:
|
||||
shell, and runtime work).
|
||||
- `tools.profile: "full"` removes profile restrictions; limit to trusted
|
||||
operator-controlled agents.
|
||||
- Per-agent `agents.list[].tools` overrides narrow or expand the root profile
|
||||
- Per-agent `agents.entries.*.tools` overrides narrow or expand the root profile
|
||||
for one agent.
|
||||
|
||||
Change the profile, restart or reload the Gateway, then recheck with
|
||||
|
||||
+4
-8
@@ -283,7 +283,7 @@ OTLP log records, persisted session transcript text, or Control UI tool
|
||||
event payloads (tool start args, partial/final result payloads, derived
|
||||
exec output, and patch summaries):
|
||||
|
||||
- `logging.redactSensitive`: `off` | `tools` (default: `tools`)
|
||||
- Sensitive-value redaction is always enabled.
|
||||
- `logging.redactPatterns`: list of regex strings that replaces the default set for log/transcript output. For Control UI tool payloads, custom patterns apply on top of the built-in defaults, so adding a pattern never weakens redaction of values already caught by the defaults.
|
||||
|
||||
File logs and session transcripts stay JSONL, but matching secret values are
|
||||
@@ -295,13 +295,9 @@ The built-in defaults cover common API credentials and payment-credential field
|
||||
names such as card number, CVC/CVV, shared payment token, and payment credential
|
||||
when they appear as JSON fields, URL parameters, CLI flags, or assignments.
|
||||
|
||||
`logging.redactSensitive: "off"` only disables this general log/transcript
|
||||
policy. OpenClaw still redacts safety-boundary payloads that can be shown to UI
|
||||
clients, support bundles, diagnostics observers, approval prompts, or agent
|
||||
tools. Examples include Control UI tool-call events, `sessions_history` output,
|
||||
diagnostics support exports, provider error observations, exec approval command
|
||||
display, and Gateway WebSocket protocol logs. Custom `logging.redactPatterns`
|
||||
can still add project-specific patterns on those surfaces.
|
||||
OpenClaw also redacts safety-boundary payloads shown to UI clients, support
|
||||
bundles, diagnostics observers, approval prompts, or agent tools. Custom
|
||||
`logging.redactPatterns` can add project-specific patterns on those surfaces.
|
||||
|
||||
## Diagnostics and OpenTelemetry
|
||||
|
||||
|
||||
+31
-55
@@ -35,7 +35,7 @@ Auto-detected whisper.cpp keeps its normal model-run logs enabled so OpenClaw ca
|
||||
|
||||
Gemini CLI auto-detect for media understanding was replaced by a sandboxed Antigravity CLI (`agy`) fallback for image/video; audio does not use a CLI fallback beyond the local binaries above.
|
||||
|
||||
To disable auto-detection, set `tools.media.audio.enabled: false`. To customize, set `tools.media.audio.models`.
|
||||
To disable auto-detection, set `tools.media.audio.enabled: false`. To customize, add capability-tagged entries to `tools.media.models`.
|
||||
|
||||
<Note>
|
||||
Binary detection is best-effort across macOS/Linux/Windows. Make sure the CLI is on `PATH` (`~` is expanded), or set an explicit CLI model with a full command path.
|
||||
@@ -48,7 +48,7 @@ openclaw capability audio providers
|
||||
openclaw doctor --lint --only core/doctor/local-audio-acceleration --severity-min info
|
||||
```
|
||||
|
||||
The provider inventory reports the local fallback winner separately from global provider selection, plus capable, requested, and observed backend fields. After transcription runs, `/status` reports the requested or observed backend in the media line. Explicit `tools.media.audio.models` CLI entries still bypass auto-selection; use their backend-specific flags such as sherpa `--provider=cuda` or whisper.cpp `--no-gpu`/`--device`.
|
||||
The provider inventory reports the local fallback winner separately from global provider selection, plus capable, requested, and observed backend fields. After transcription runs, `/status` reports the requested or observed backend in the media line. Explicit audio-capable `tools.media.models` CLI entries still bypass auto-selection; use their backend-specific flags such as sherpa `--provider=cuda` or whisper.cpp `--no-gpu`/`--device`.
|
||||
|
||||
## Config examples
|
||||
|
||||
@@ -58,38 +58,17 @@ The provider inventory reports the local fallback winner separately from global
|
||||
{
|
||||
tools: {
|
||||
media: {
|
||||
audio: {
|
||||
enabled: true,
|
||||
maxBytes: 20971520,
|
||||
models: [
|
||||
{ provider: "openai", model: "gpt-4o-transcribe" },
|
||||
{
|
||||
type: "cli",
|
||||
command: "whisper",
|
||||
args: ["--model", "base", "{{MediaPath}}"],
|
||||
timeoutSeconds: 45,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Provider-only with scope gating
|
||||
|
||||
```json5
|
||||
{
|
||||
tools: {
|
||||
media: {
|
||||
audio: {
|
||||
enabled: true,
|
||||
scope: {
|
||||
default: "allow",
|
||||
rules: [{ action: "deny", match: { chatType: "group" } }],
|
||||
models: [
|
||||
{ provider: "openai", model: "gpt-4o-transcribe", capabilities: ["audio"] },
|
||||
{
|
||||
type: "cli",
|
||||
command: "whisper",
|
||||
args: ["--model", "base", "{{MediaPath}}"],
|
||||
timeoutSeconds: 45,
|
||||
capabilities: ["audio"],
|
||||
},
|
||||
models: [{ provider: "openai", model: "gpt-4o-transcribe" }],
|
||||
},
|
||||
],
|
||||
audio: { enabled: true, preferredModel: "openai/gpt-4o-transcribe" },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -101,10 +80,8 @@ The provider inventory reports the local fallback winner separately from global
|
||||
{
|
||||
tools: {
|
||||
media: {
|
||||
audio: {
|
||||
enabled: true,
|
||||
models: [{ provider: "deepgram", model: "nova-3" }],
|
||||
},
|
||||
models: [{ provider: "deepgram", model: "nova-3", capabilities: ["audio"] }],
|
||||
audio: { enabled: true },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -116,10 +93,8 @@ The provider inventory reports the local fallback winner separately from global
|
||||
{
|
||||
tools: {
|
||||
media: {
|
||||
audio: {
|
||||
enabled: true,
|
||||
models: [{ provider: "mistral", model: "voxtral-mini-latest" }],
|
||||
},
|
||||
models: [{ provider: "mistral", model: "voxtral-mini-latest", capabilities: ["audio"] }],
|
||||
audio: { enabled: true },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -131,10 +106,14 @@ The provider inventory reports the local fallback winner separately from global
|
||||
{
|
||||
tools: {
|
||||
media: {
|
||||
audio: {
|
||||
enabled: true,
|
||||
models: [{ provider: "senseaudio", model: "senseaudio-asr-pro-1.5-260319" }],
|
||||
},
|
||||
models: [
|
||||
{
|
||||
provider: "senseaudio",
|
||||
model: "senseaudio-asr-pro-1.5-260319",
|
||||
capabilities: ["audio"],
|
||||
},
|
||||
],
|
||||
audio: { enabled: true },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -148,9 +127,8 @@ The provider inventory reports the local fallback winner separately from global
|
||||
media: {
|
||||
audio: {
|
||||
enabled: true,
|
||||
echoTranscript: true, // default is false
|
||||
echoFormat: '📝 "{transcript}"', // optional, supports {transcript}
|
||||
models: [{ provider: "openai", model: "gpt-4o-transcribe" }],
|
||||
echoTranscript: true,
|
||||
echoFormat: '📝 "{transcript}"',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -164,17 +142,15 @@ The provider inventory reports the local fallback winner separately from global
|
||||
- Deepgram picks up `DEEPGRAM_API_KEY` when `provider: "deepgram"` is used. Setup details: [Deepgram](/providers/deepgram).
|
||||
- Mistral setup details: [Mistral](/providers/mistral).
|
||||
- SenseAudio picks up `SENSEAUDIO_API_KEY` when `provider: "senseaudio"` is used. Setup details: [SenseAudio](/providers/senseaudio).
|
||||
- Audio providers can override `baseUrl`, `headers`, and `providerOptions` via `tools.media.audio`.
|
||||
- Default size cap is 20MB (`tools.media.audio.maxBytes`). Oversize audio is skipped for that model and the next entry is tried.
|
||||
- Audio providers can use defaults under `tools.media.audio` or override `baseUrl`, `headers`, `providerOptions`, and limits on their `tools.media.models[]` entry.
|
||||
- The built-in audio size cap is 20MB. An entry-level `maxBytes` override can change it; oversize audio is skipped for that model and the next entry is tried.
|
||||
- Audio files below 1024 bytes are skipped before provider/CLI transcription.
|
||||
- Default `maxChars` for audio is **unset** (full transcript). Set `tools.media.audio.maxChars` or a per-entry `maxChars` to trim output.
|
||||
- Default `maxChars` for audio is **unset** (full transcript). Set `tools.media.audio.maxChars` or per-entry `maxChars` to trim output.
|
||||
- OpenAI auto-detect default is `gpt-4o-transcribe`; set `model: "gpt-4o-mini-transcribe"` for a cheaper/faster option.
|
||||
- Use `tools.media.audio.attachments` to process multiple voice notes (`mode: "all"` plus `maxAttachments`, default 1).
|
||||
- Transcript is available to templates as `{{Transcript}}`.
|
||||
- `tools.media.audio.echoTranscript` is off by default; enable it to send a transcript confirmation back to the originating chat before agent processing.
|
||||
- `tools.media.audio.echoFormat` customizes the echo text (placeholder: `{transcript}`; default `📝 "{transcript}"`).
|
||||
- `tools.media.audio.echoTranscript` is off by default; `echoFormat` accepts a `{transcript}` placeholder.
|
||||
- CLI stdout is capped at 5MB; keep CLI output concise.
|
||||
- CLI `args` should use `{{MediaPath}}` for the local audio file path. Run `openclaw doctor --fix` to migrate deprecated `{input}` placeholders from older `audio.transcription.command` configs (retired key: `audio.transcription`, replaced by `tools.media.audio.models`).
|
||||
- CLI `args` should use `{{MediaPath}}` for the local audio file path. Run `openclaw doctor --fix` to migrate deprecated `{input}` placeholders from older `audio.transcription.command` configs (retired key: `audio.transcription`, replaced by `tools.media.models`).
|
||||
- `tools.media.concurrency` bounds media tasks; it is not a GPU scheduler.
|
||||
|
||||
### Resident local STT
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -89,7 +89,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 node app still enforces its platform-specific settings and OS permissions, including **Allow Computer Control**, Accessibility, and Screen Recording on macOS.
|
||||
|
||||
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.
|
||||
|
||||
@@ -57,7 +57,7 @@ The 16MB audio/video and 100MB document figures above are the shared per-kind me
|
||||
- Audio sets `{{Transcript}}` and uses the transcript for command parsing so slash commands still work.
|
||||
- Video and image descriptions preserve any caption text for command parsing.
|
||||
- If the active primary model already supports vision natively, OpenClaw skips the `[Image]` summary block and passes the original image to the model instead.
|
||||
- By default only the first matching image/audio/video attachment is processed; set `tools.media.<capability>.attachments` to process multiple attachments.
|
||||
- By default only the first matching image/audio/video attachment is processed; use `tools.media.<capability>.attachments` to select multiple attachments.
|
||||
|
||||
## Limits and errors
|
||||
|
||||
@@ -70,9 +70,10 @@ The 16MB audio/video and 100MB document figures above are the shared per-kind me
|
||||
|
||||
**Media understanding caps (transcription/description)**
|
||||
|
||||
- Image default: 10MB (`tools.media.image.maxBytes`).
|
||||
- Audio default: 20MB (`tools.media.audio.maxBytes`).
|
||||
- Video default: 50MB (`tools.media.video.maxBytes`).
|
||||
- Image default: 10MB (override with `tools.media.image.maxBytes`, or per
|
||||
`tools.media.models[]` entry with `maxBytes`).
|
||||
- Audio default: 20MB (override with `tools.media.audio.maxBytes`, or per entry).
|
||||
- Video default: 50MB (override with `tools.media.video.maxBytes`, or per entry).
|
||||
- Oversize media skips understanding, but the reply still goes through with the original body.
|
||||
|
||||
## Notes for Tests
|
||||
|
||||
+22
-20
@@ -194,7 +194,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
|
||||
|
||||
@@ -228,7 +228,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
|
||||
|
||||
@@ -266,7 +266,7 @@ Configure defaults (gateway config):
|
||||
|
||||
```bash
|
||||
openclaw config set tools.exec.host node
|
||||
openclaw config set tools.exec.security allowlist
|
||||
openclaw config set tools.exec.mode allowlist
|
||||
openclaw config set tools.exec.node "<id-or-name>"
|
||||
```
|
||||
|
||||
@@ -450,7 +450,7 @@ Node commands must pass two gates before they can be invoked:
|
||||
1. The node must declare the command in its authenticated connect metadata (`connect.commands`).
|
||||
2. The gateway's platform-and-approval-derived allowlist must include the declared command.
|
||||
|
||||
Default allowlists by platform (before plugin defaults and `allowCommands`/`denyCommands` overrides):
|
||||
Default allowlists by platform (before plugin defaults and `commands.allow`/`commands.deny` overrides):
|
||||
|
||||
| Platform | Commands allowed by default |
|
||||
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -469,9 +469,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/Linux) 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 capability, tool-policy, arming, and platform-fulfiller 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 capability, tool-policy, arming, and platform-fulfiller 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.
|
||||
|
||||
@@ -497,9 +497,11 @@ Node-related settings live under `gateway.nodes` and `tools.exec`:
|
||||
enabled: true,
|
||||
},
|
||||
// Opt into dangerous/privacy-heavy node commands (camera.snap, etc.).
|
||||
allowCommands: ["camera.snap", "screen.record"],
|
||||
// Block exact command names even if defaults or allowCommands include them.
|
||||
denyCommands: ["camera.clip"],
|
||||
commands: {
|
||||
allow: ["camera.snap", "screen.record"],
|
||||
// Block exact command names even if defaults or commands.allow include them.
|
||||
deny: ["camera.clip"],
|
||||
},
|
||||
},
|
||||
},
|
||||
tools: {
|
||||
@@ -515,7 +517,7 @@ Node-related settings live under `gateway.nodes` and `tools.exec`:
|
||||
}
|
||||
```
|
||||
|
||||
Use exact node command names. `denyCommands` removes a command even when a platform default or `allowCommands` entry would otherwise allow it. Paired nodes may publish agent-visible plugin tool descriptors by default, but each descriptor's command must still be in the node's approved command surface. Set `gateway.nodes.pluginTools.enabled: false` to ignore all such descriptors. See [Gateway configuration reference](/gateway/configuration-reference#gateway) for gateway node pairing and command-policy field details.
|
||||
Use exact node command names. `commands.deny` removes a command even when a platform default or `commands.allow` entry would otherwise allow it. Paired nodes may publish agent-visible plugin tool descriptors by default, but each descriptor's command must still be in the node's approved command surface. Set `gateway.nodes.pluginTools.enabled: false` to ignore all such descriptors. See [Gateway configuration reference](/gateway/configuration-reference#gateway) for gateway node pairing and command-policy field details.
|
||||
|
||||
Per-agent exec node override:
|
||||
|
||||
@@ -633,7 +635,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`:
|
||||
|
||||
@@ -641,7 +643,7 @@ For read-only SMS search, opt in explicitly in `openclaw.json`:
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
allowCommands: ["sms.search"],
|
||||
commands: { allow: ["sms.search"] },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -659,7 +661,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
|
||||
|
||||
@@ -672,9 +674,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.
|
||||
|
||||
@@ -705,7 +707,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.
|
||||
@@ -727,15 +729,15 @@ openclaw config set tools.exec.node "node-id-or-name"
|
||||
Per-agent override:
|
||||
|
||||
```bash
|
||||
openclaw config get agents.list
|
||||
openclaw config set 'agents.list[0].tools.exec.node' "node-id-or-name"
|
||||
openclaw config get agents.entries
|
||||
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.list[0].tools.exec.node'
|
||||
openclaw config unset 'agents.entries.main.tools.exec.node'
|
||||
```
|
||||
|
||||
## Permissions map
|
||||
|
||||
@@ -33,21 +33,20 @@ Vendor plugins register capability metadata (which provider supports which media
|
||||
|
||||
## Config
|
||||
|
||||
`tools.media` holds a shared model list plus per-capability overrides:
|
||||
`tools.media` holds one capability-tagged model list plus small per-capability controls:
|
||||
|
||||
```json5
|
||||
{
|
||||
tools: {
|
||||
media: {
|
||||
concurrency: 2, // max concurrent capability runs (default)
|
||||
models: [/* shared list, gate with capabilities */],
|
||||
image: {/* optional overrides */},
|
||||
audio: {
|
||||
/* optional overrides */
|
||||
echoTranscript: true,
|
||||
echoFormat: '📝 "{transcript}"',
|
||||
},
|
||||
video: {/* optional overrides */},
|
||||
models: [
|
||||
{ provider: "openai", model: "gpt-4o-mini-transcribe", capabilities: ["audio"] },
|
||||
{ provider: "google", model: "gemini-3-flash-preview", capabilities: ["image", "video"] },
|
||||
],
|
||||
image: { preferredModel: "google/gemini-3-flash-preview" },
|
||||
audio: { enabled: true },
|
||||
video: { enabled: true },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -55,22 +54,21 @@ Vendor plugins register capability metadata (which provider supports which media
|
||||
|
||||
Per-capability (`image`/`audio`/`video`) keys:
|
||||
|
||||
| Key | Type | Default | Notes |
|
||||
| ----------------------------------------------- | --------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
||||
| `enabled` | `boolean` | auto (`false` disables) | Set `false` to turn off auto-detect for this capability |
|
||||
| `models` | array | none | Preferred before the shared `tools.media.models` list |
|
||||
| `prompt` | `string` | `"Describe the {media}."` (+ maxChars guidance) | Image/video only by default |
|
||||
| `maxChars` | `number` | `500` (image/video), unset (audio) | Output is trimmed if the model returns more |
|
||||
| `maxBytes` | `number` | image `10485760`, audio `20971520`, video `52428800` | Oversized media skips to the next model |
|
||||
| `timeoutSeconds` | `number` | `60` (image/audio), `120` (video) | |
|
||||
| `language` | `string` | unset | Audio transcription hint |
|
||||
| `baseUrl`/`headers`/`providerOptions`/`request` | - | - | Provider request overrides; see [Tools and custom providers](/gateway/config-tools) |
|
||||
| `attachments` | object | `{ mode: "first", maxAttachments: 1 }` | See [Attachment policy](#attachment-policy) |
|
||||
| `scope` | object | unset | Gate by channel/chatType/keyPrefix |
|
||||
| `echoTranscript` | `boolean` | `false` | Audio only: echo the transcript back to the chat before agent processing |
|
||||
| `echoFormat` | `string` | `'📝 "{transcript}"'` | Audio only: `{transcript}` placeholder |
|
||||
| Key | Type | Default | Notes |
|
||||
| ---------------- | --------- | -------------------------------------- | -------------------------------------------------------------------- |
|
||||
| `enabled` | `boolean` | auto (`false` disables) | Set `false` to turn off auto-detect for this capability |
|
||||
| `preferredModel` | `string` | first compatible entry | Prefer `provider/model`, model id, `provider:<id>`, or `cli:command` |
|
||||
| `prompt` | `string` | capability default | Default prompt when an entry does not override it |
|
||||
| `maxChars` | `number` | `500` image/video, unset audio | Default output limit |
|
||||
| `maxBytes` | `number` | 10MB image, 20MB audio, 50MB video | Default input limit |
|
||||
| `timeoutSeconds` | `number` | `60` image/audio, `120` video | Default request timeout |
|
||||
| `language` | `string` | unset | Audio transcription hint |
|
||||
| `scope` | object | unset | Gate by channel/chat type/source key |
|
||||
| `attachments` | object | `{ mode: "first", maxAttachments: 1 }` | Select which matching attachments are processed |
|
||||
| `echoTranscript` | `boolean` | `false` | Audio only: echo the transcript before agent processing |
|
||||
| `echoFormat` | `string` | `'📝 "{transcript}"'` | Audio only: format for the echoed transcript |
|
||||
|
||||
Deepgram-specific options go under `providerOptions.deepgram` (the top-level `deepgram: { detectLanguage, punctuate, smartFormat }` field is deprecated but still read).
|
||||
Prompts, limits, language hints, request overrides, and provider options can be set as capability defaults or overridden on individual `tools.media.models[]` entries. Capability defaults also cover auto-detected providers when no explicit model is configured.
|
||||
|
||||
### Model entries
|
||||
|
||||
@@ -87,7 +85,7 @@ Each `models[]` entry is a **provider** entry (default) or a **CLI** entry:
|
||||
maxChars: 500,
|
||||
maxBytes: 10485760,
|
||||
timeoutSeconds: 60,
|
||||
capabilities: ["image"], // optional, for multi-modal shared entries
|
||||
capabilities: ["image"],
|
||||
profile: "vision-profile",
|
||||
preferredProfile: "vision-fallback",
|
||||
}
|
||||
@@ -119,7 +117,7 @@ Each `models[]` entry is a **provider** entry (default) or a **CLI** entry:
|
||||
|
||||
### Provider credentials
|
||||
|
||||
Provider media understanding uses the same auth resolution as normal model calls: auth profiles, environment variables, then `models.providers.<providerId>.apiKey`. `tools.media.*.models[]` entries do not accept an inline `apiKey` field.
|
||||
Provider media understanding uses the same auth resolution as normal model calls: auth profiles, environment variables, then `models.providers.<providerId>.apiKey`. `tools.media.models[]` entries do not accept an inline `apiKey` field.
|
||||
|
||||
```json5
|
||||
{
|
||||
|
||||
@@ -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` / `gateway.nodes.commands.deny` 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 `gateway.nodes.commands.allow`.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -32,21 +32,21 @@ 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
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
allowCommands: ["health.summary"],
|
||||
commands: { allow: ["health.summary"] },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`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`
|
||||
|
||||
|
||||
@@ -150,7 +150,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.
|
||||
|
||||
|
||||
@@ -444,7 +444,7 @@ const voices = await api.runtime.tts.listVoices({
|
||||
Notes:
|
||||
|
||||
- `textToSpeech` returns the normal core TTS output payload for file/voice-note surfaces.
|
||||
- Uses core `messages.tts` configuration and provider selection.
|
||||
- Uses core `tts` configuration and provider selection.
|
||||
- Returns PCM audio buffer + sample rate. Plugins must resample/encode for providers.
|
||||
- `listVoices` is optional per provider. Use it for vendor-owned voice pickers or setup flows.
|
||||
- Core passes a resolved request deadline to provider `listVoices` hooks; provider-specific timeout settings may override it.
|
||||
|
||||
@@ -133,7 +133,7 @@ runtime behavior. Runtime behavior starts when the plugin entry calls
|
||||
output: "json",
|
||||
input: "stdin",
|
||||
modelArg: "--model",
|
||||
sessionArg: "--session",
|
||||
sessionArgs: ["--session", "{sessionId}"],
|
||||
sessionMode: "existing",
|
||||
sessionIdFields: ["session_id", "conversation_id"],
|
||||
systemPromptFileArg: "--system-file",
|
||||
@@ -185,7 +185,7 @@ runtime behavior. Runtime behavior starts when the plugin entry calls
|
||||
| `env` / `clearEnv` | Extra env vars to inject, or names to strip before launch |
|
||||
| `modelArg` | Flag used before the model id |
|
||||
| `modelAliases` | Map OpenClaw model ids to CLI-native ids |
|
||||
| `sessionArg` / `sessionArgs` | How to pass a session id |
|
||||
| `sessionArgs` | How to pass a session id using `{sessionId}` |
|
||||
| `sessionMode` | `always`, `existing`, or `none` |
|
||||
| `sessionIdFields` | JSON fields OpenClaw reads from CLI output |
|
||||
| `systemPromptArg` / `systemPromptFileArg` | System prompt transport |
|
||||
|
||||
@@ -527,10 +527,10 @@ 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, `tools.media.image.timeoutSeconds`
|
||||
- For the media-understanding `image` tool, the selected image-capable `tools.media.models[]` entry's `timeoutSeconds`
|
||||
converted to milliseconds, or the 60 second media default. For image
|
||||
understanding, this applies to the request itself and is not reduced by
|
||||
earlier preparation work.
|
||||
|
||||
@@ -317,8 +317,8 @@ 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`,
|
||||
`videoGenerationModel`, `pdfModel`, and `messages.tts`.
|
||||
settings such as `agents.defaults.mediaModels.image`,
|
||||
`agents.defaults.mediaModels.video`, `pdfModel`, and `tts`.
|
||||
|
||||
Text, images, video, music, TTS, approvals, and messaging-tool output continue
|
||||
through the normal OpenClaw delivery path; media generation does not require
|
||||
|
||||
@@ -764,10 +764,10 @@ 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 `tools.media.image.timeoutSeconds` or its 60 second media default; for
|
||||
uses the selected image-capable `tools.media.models[]` entry's `timeoutSeconds` or its 60 second media default; for
|
||||
image understanding, that timeout applies to the request itself and is not
|
||||
reduced by earlier preparation work. On timeout, OpenClaw aborts the tool
|
||||
signal where supported and returns a failed dynamic-tool response to Codex
|
||||
|
||||
+10
-12
@@ -199,7 +199,7 @@ Route Meet through that node:
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
allowCommands: ["googlemeet.chrome", "browser.proxy"],
|
||||
commands: { allow: ["googlemeet.chrome", "browser.proxy"] },
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
@@ -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. |
|
||||
@@ -762,14 +762,12 @@ ElevenLabs for both agent-mode listening and speaking:
|
||||
|
||||
```json5
|
||||
{
|
||||
messages: {
|
||||
tts: {
|
||||
provider: "elevenlabs",
|
||||
providers: {
|
||||
elevenlabs: {
|
||||
modelId: "eleven_v3",
|
||||
speakerVoiceId: "pMsXgVXv3BLzUgSXRplE",
|
||||
},
|
||||
tts: {
|
||||
provider: "elevenlabs",
|
||||
providers: {
|
||||
elevenlabs: {
|
||||
modelId: "eleven_v3",
|
||||
speakerVoiceId: "pMsXgVXv3BLzUgSXRplE",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -795,7 +793,7 @@ ElevenLabs for both agent-mode listening and speaking:
|
||||
}
|
||||
```
|
||||
|
||||
The persistent Meet voice comes from `messages.tts.providers.elevenlabs.speakerVoiceId`. Agent replies can also use per-reply `[[tts:speakerVoiceId=... model=eleven_v3]]` directives when TTS model overrides are enabled, but config is the deterministic default for meetings. On join, logs show `transcriptionProvider=elevenlabs`, and each spoken reply logs `provider=elevenlabs model=eleven_v3 speakerVoiceId=<voiceId>`.
|
||||
The persistent Meet voice comes from `tts.providers.elevenlabs.speakerVoiceId`. Agent replies can also use per-reply `[[tts:speakerVoiceId=... model=eleven_v3]]` directives when TTS model overrides are enabled, but config is the deterministic default for meetings. On join, logs show `transcriptionProvider=elevenlabs`, and each spoken reply logs `provider=elevenlabs model=eleven_v3 speakerVoiceId=<voiceId>`.
|
||||
|
||||
Twilio-only config:
|
||||
|
||||
@@ -1038,7 +1036,7 @@ The node must be connected and list `googlemeet.chrome` plus `browser.proxy`; th
|
||||
{
|
||||
gateway: {
|
||||
nodes: {
|
||||
allowCommands: ["browser.proxy", "googlemeet.chrome"],
|
||||
commands: { allow: ["browser.proxy", "googlemeet.chrome"] },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ summary: "Run local GGUF text inference and memory embeddings in OpenClaw with l
|
||||
read_when:
|
||||
- You want local text inference without an API key or model server
|
||||
- You want memory search embeddings from a local GGUF model
|
||||
- You are configuring memorySearch.provider = "local"
|
||||
- You are configuring memory.search.provider = "local"
|
||||
- You need the OpenClaw plugin that owns the node-llama-cpp runtime
|
||||
title: "llama.cpp Provider"
|
||||
sidebarTitle: "llama.cpp Provider"
|
||||
@@ -95,17 +95,15 @@ own read-only cache resolver, including repository, branch, and split-file namin
|
||||
|
||||
## Memory embedding configuration
|
||||
|
||||
Set `memorySearch.provider` to `local`:
|
||||
Set `memory.search.provider` to `local`:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "local",
|
||||
local: {
|
||||
modelPath: "hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/embeddinggemma-300m-qat-Q8_0.gguf",
|
||||
},
|
||||
memory: {
|
||||
search: {
|
||||
provider: "local",
|
||||
local: {
|
||||
modelPath: "hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/embeddinggemma-300m-qat-Q8_0.gguf",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -156,5 +154,5 @@ with:
|
||||
|
||||
For local inference without an in-process native dependency, use the Ollama or
|
||||
LM Studio provider instead. For lower-friction local embeddings, set
|
||||
`memorySearch.provider` to a remote embedding provider such as `lmstudio`,
|
||||
`memory.search.provider` to a remote embedding provider such as `lmstudio`,
|
||||
`ollama`, `openai`, or `voyage` instead.
|
||||
|
||||
@@ -174,8 +174,7 @@ Gateway restarts; use `captureEnabled: false` for a persistent stop.
|
||||
Logbook resolves the observation model in this order:
|
||||
|
||||
1. `plugins.entries.logbook.config.visionModel`
|
||||
2. the first image-capable Codex entry under `tools.media.image.models`
|
||||
3. the first image-capable Codex entry under `tools.media.models`
|
||||
2. the first image-capable Codex entry under `tools.media.models`
|
||||
|
||||
Other media providers are skipped because they do not currently expose the
|
||||
structured extraction contract Logbook requires. Setting
|
||||
@@ -227,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
|
||||
@@ -259,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
|
||||
|
||||
@@ -33,7 +33,7 @@ but only one plugin owns the active memory slot at a time.
|
||||
|
||||
<Note>
|
||||
LanceDB's `memory_recall` does not receive the protected private transcript
|
||||
authorization used by `memorySearch.rememberAcrossConversations`. Use LanceDB's
|
||||
authorization used by `memory.search.rememberAcrossConversations`. Use LanceDB's
|
||||
`autoRecall` or its `memory_recall` tool through
|
||||
[advanced Active Memory](/concepts/active-memory#lancedb-memory).
|
||||
`openclaw doctor` reports when Remember across conversations is unavailable
|
||||
@@ -217,8 +217,8 @@ and caps at 3 captured memories per agent turn.
|
||||
|
||||
Every memory is owned by one agent. Recall, duplicate detection, capture,
|
||||
listing, raw queries, and deletion all enforce that owner before returning or
|
||||
mutating rows. An agent with `memorySearch.enabled: false` (in `agents.list[]`
|
||||
or via `agents.defaults`) also gets none of the `memory_recall`, `memory_store`,
|
||||
mutating rows. An agent with `memory.search.enabled: false` in its `agents.entries.*`
|
||||
entry, or one inheriting a disabled top-level search, also gets none of the `memory_recall`, `memory_store`,
|
||||
or `memory_forget` tools and does not participate in automatic recall or
|
||||
capture, even when the plugin-level `autoRecall`/`autoCapture` flags are on.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user