diff --git a/docs/docs.json b/docs/docs.json
index e39cf3f25a8..121125c6b98 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -1025,43 +1025,6 @@
"group": "Overview",
"pages": ["tools/index"]
},
- {
- "group": "Built-in tools",
- "pages": [
- "tools/exec",
- "tools/exec-approvals",
- "tools/elevated",
- "tools/apply-patch",
- "tools/web",
- "tools/brave-search",
- "tools/firecrawl",
- "tools/tavily",
- "tools/perplexity-search",
- "tools/pdf",
- "tools/reactions",
- "tools/thinking",
- "tools/loop-detection",
- "tools/btw"
- ]
- },
- {
- "group": "Browser",
- "pages": [
- "tools/browser",
- "tools/browser-login",
- "tools/browser-linux-troubleshooting",
- "tools/browser-wsl2-windows-remote-cdp-troubleshooting"
- ]
- },
- {
- "group": "Agent coordination",
- "pages": [
- "tools/agent-send",
- "tools/subagents",
- "tools/acp-agents",
- "tools/multi-agent-sandbox-tools"
- ]
- },
{
"group": "Plugins",
"pages": [
@@ -1087,10 +1050,6 @@
"prose"
]
},
- {
- "group": "Plugin tools",
- "pages": ["tools/diffs", "tools/llm-task", "tools/lobster"]
- },
{
"group": "Automation",
"pages": [
@@ -1106,18 +1065,43 @@
]
},
{
- "group": "Media and devices",
+ "group": "Tools",
"pages": [
- "nodes/index",
- "nodes/troubleshooting",
- "nodes/media-understanding",
- "nodes/images",
- "nodes/audio",
- "nodes/camera",
- "nodes/talk",
- "nodes/voicewake",
- "nodes/location-command",
- "tools/tts"
+ "tools/exec",
+ "tools/exec-approvals",
+ "tools/elevated",
+ "tools/apply-patch",
+ "tools/web",
+ "tools/brave-search",
+ "tools/firecrawl",
+ "tools/tavily",
+ "tools/perplexity-search",
+ "tools/pdf",
+ "tools/diffs",
+ "tools/llm-task",
+ "tools/lobster",
+ "tools/reactions",
+ "tools/thinking",
+ "tools/loop-detection",
+ "tools/btw"
+ ]
+ },
+ {
+ "group": "Browser",
+ "pages": [
+ "tools/browser",
+ "tools/browser-login",
+ "tools/browser-linux-troubleshooting",
+ "tools/browser-wsl2-windows-remote-cdp-troubleshooting"
+ ]
+ },
+ {
+ "group": "Agent coordination",
+ "pages": [
+ "tools/agent-send",
+ "tools/subagents",
+ "tools/acp-agents",
+ "tools/multi-agent-sandbox-tools"
]
}
]
@@ -1287,6 +1271,21 @@
"security/CONTRIBUTING-THREAT-MODEL"
]
},
+ {
+ "group": "Nodes and devices",
+ "pages": [
+ "nodes/index",
+ "nodes/troubleshooting",
+ "nodes/media-understanding",
+ "nodes/images",
+ "nodes/audio",
+ "nodes/camera",
+ "nodes/talk",
+ "nodes/voicewake",
+ "nodes/location-command",
+ "tools/tts"
+ ]
+ },
{
"group": "Web interfaces",
"pages": ["web/index", "web/control-ui", "web/dashboard", "web/webchat", "web/tui"]
diff --git a/docs/tools/agent-send.md b/docs/tools/agent-send.md
index e301feeea12..153a1e9b3c6 100644
--- a/docs/tools/agent-send.md
+++ b/docs/tools/agent-send.md
@@ -1,53 +1,100 @@
---
-summary: "Direct `openclaw agent` CLI runs (with optional delivery)"
+summary: "Run agent turns from the CLI and optionally deliver replies to channels"
read_when:
- - Adding or modifying the agent CLI entrypoint
+ - You want to trigger agent runs from scripts or the command line
+ - You need to deliver agent replies to a chat channel programmatically
title: "Agent Send"
---
-# `openclaw agent` (direct agent runs)
+# Agent Send
-`openclaw agent` runs a single agent turn without needing an inbound chat message.
-By default it goes **through the Gateway**; add `--local` to force the embedded
-runtime on the current machine.
+`openclaw agent` runs a single agent turn from the command line without needing
+an inbound chat message. Use it for scripted workflows, testing, and
+programmatic delivery.
+
+## Quick start
+
+
+
+ ```bash
+ openclaw agent --message "What is the weather today?"
+ ```
+
+ This sends the message through the Gateway and prints the reply.
+
+
+
+
+ ```bash
+ # Target a specific agent
+ openclaw agent --agent ops --message "Summarize logs"
+
+ # Target a phone number (derives session key)
+ openclaw agent --to +15555550123 --message "Status update"
+
+ # Reuse an existing session
+ openclaw agent --session-id abc123 --message "Continue the task"
+ ```
+
+
+
+
+ ```bash
+ # Deliver to WhatsApp (default channel)
+ openclaw agent --to +15555550123 --message "Report ready" --deliver
+
+ # Deliver to Slack
+ openclaw agent --agent ops --message "Generate report" \
+ --deliver --reply-channel slack --reply-to "#reports"
+ ```
+
+
+
+
+## Flags
+
+| Flag | Description |
+| ----------------------------- | ----------------------------------------------------------- |
+| `--message \` | Message to send (required) |
+| `--to \` | Derive session key from a target (phone, chat id) |
+| `--agent \` | Target a configured agent (uses its `main` session) |
+| `--session-id \` | Reuse an existing session by id |
+| `--local` | Force local embedded runtime (skip Gateway) |
+| `--deliver` | Send the reply to a chat channel |
+| `--channel \` | Delivery channel (whatsapp, telegram, discord, slack, etc.) |
+| `--reply-to \` | Delivery target override |
+| `--reply-channel \` | Delivery channel override |
+| `--reply-account \` | Delivery account id override |
+| `--thinking \` | Set thinking level (off, minimal, low, medium, high, xhigh) |
+| `--verbose \` | Set verbose level |
+| `--timeout \` | Override agent timeout |
+| `--json` | Output structured JSON |
## Behavior
-- Required: `--message `
-- Session selection:
- - `--to ` derives the session key (group/channel targets preserve isolation; direct chats collapse to `main`), **or**
- - `--session-id ` reuses an existing session by id, **or**
- - `--agent ` targets a configured agent directly (uses that agent's `main` session key)
-- Runs the same embedded agent runtime as normal inbound replies.
-- Thinking/verbose flags persist into the session store.
-- Output:
- - default: prints reply text (plus `MEDIA:` lines)
- - `--json`: prints structured payload + metadata
-- Optional delivery back to a channel with `--deliver` + `--channel` (target formats match `openclaw message --target`).
-- Use `--reply-channel`/`--reply-to`/`--reply-account` to override delivery without changing the session.
-
-If the Gateway is unreachable, the CLI **falls back** to the embedded local run.
+- By default, the CLI goes **through the Gateway**. Add `--local` to force the
+ embedded runtime on the current machine.
+- If the Gateway is unreachable, the CLI **falls back** to the local embedded run.
+- Session selection: `--to` derives the session key (group/channel targets
+ preserve isolation; direct chats collapse to `main`).
+- Thinking and verbose flags persist into the session store.
+- Output: plain text by default, or `--json` for structured payload + metadata.
## Examples
```bash
-openclaw agent --to +15555550123 --message "status update"
-openclaw agent --agent ops --message "Summarize logs"
-openclaw agent --session-id 1234 --message "Summarize inbox" --thinking medium
+# Simple turn with JSON output
openclaw agent --to +15555550123 --message "Trace logs" --verbose on --json
-openclaw agent --to +15555550123 --message "Summon reply" --deliver
-openclaw agent --agent ops --message "Generate report" --deliver --reply-channel slack --reply-to "#reports"
+
+# Turn with thinking level
+openclaw agent --session-id 1234 --message "Summarize inbox" --thinking medium
+
+# Deliver to a different channel than the session
+openclaw agent --agent ops --message "Alert" --deliver --reply-channel telegram --reply-to "@admin"
```
-## Flags
+## Related
-- `--local`: run locally (requires model provider API keys in your shell)
-- `--deliver`: send the reply to the chosen channel
-- `--channel`: delivery channel (`whatsapp|telegram|discord|googlechat|slack|signal|imessage`, default: `whatsapp`)
-- `--reply-to`: delivery target override
-- `--reply-channel`: delivery channel override
-- `--reply-account`: delivery account id override
-- `--thinking `: persist thinking level (GPT-5.2 + Codex models only)
-- `--verbose `: persist verbose level
-- `--timeout `: override agent timeout
-- `--json`: output structured JSON
+- [Agent CLI reference](/cli/agent)
+- [Sub-agents](/tools/subagents) — background sub-agent spawning
+- [Sessions](/concepts/session) — how session keys work
diff --git a/docs/tools/creating-skills.md b/docs/tools/creating-skills.md
index 964165ad0a2..69024038efc 100644
--- a/docs/tools/creating-skills.md
+++ b/docs/tools/creating-skills.md
@@ -6,53 +6,112 @@ read_when:
- You need a quick starter workflow for SKILL.md-based skills
---
-# Creating Custom Skills 🛠
+# Creating Skills
-OpenClaw is designed to be easily extensible. "Skills" are the primary way to add new capabilities to your assistant.
+Skills teach the agent how and when to use tools. Each skill is a directory
+containing a `SKILL.md` file with YAML frontmatter and markdown instructions.
-## What is a Skill?
+For how skills are loaded and prioritized, see [Skills](/tools/skills).
-A skill is a directory containing a `SKILL.md` file (which provides instructions and tool definitions to the LLM) and optionally some scripts or resources.
+## Create your first skill
-## Step-by-Step: Your First Skill
+
+
+ Skills live in your workspace. Create a new folder:
-### 1. Create the Directory
+ ```bash
+ mkdir -p ~/.openclaw/workspace/skills/hello-world
+ ```
-Skills live in your workspace, usually `~/.openclaw/workspace/skills/`. Create a new folder for your skill:
+
-```bash
-mkdir -p ~/.openclaw/workspace/skills/hello-world
-```
+
+ Create `SKILL.md` inside that directory. The frontmatter defines metadata,
+ and the markdown body contains instructions for the agent.
-### 2. Define the `SKILL.md`
+ ```markdown
+ ---
+ name: hello_world
+ description: A simple skill that says hello.
+ ---
-Create a `SKILL.md` file in that directory. This file uses YAML frontmatter for metadata and Markdown for instructions.
+ # Hello World Skill
-```markdown
----
-name: hello_world
-description: A simple skill that says hello.
----
+ When the user asks for a greeting, use the `echo` tool to say
+ "Hello from your custom skill!".
+ ```
-# Hello World Skill
+
-When the user asks for a greeting, use the `echo` tool to say "Hello from your custom skill!".
-```
+
+ You can define custom tool schemas in the frontmatter or instruct the agent
+ to use existing system tools (like `exec` or `browser`). Skills can also
+ ship inside plugins alongside the tools they document.
-### 3. Add Tools (Optional)
+
-You can define custom tools in the frontmatter or instruct the agent to use existing system tools (like `bash` or `browser`).
+
+ Start a new session so OpenClaw picks up the skill:
-### 4. Refresh OpenClaw
+ ```bash
+ # From chat
+ /new
-Ask your agent to "refresh skills" or restart the gateway. OpenClaw will discover the new directory and index the `SKILL.md`.
+ # Or restart the gateway
+ openclaw gateway restart
+ ```
-## Best Practices
+ Verify the skill loaded:
-- **Be Concise**: Instruct the model on _what_ to do, not how to be an AI.
-- **Safety First**: If your skill uses `bash`, ensure the prompts don't allow arbitrary command injection from untrusted user input.
-- **Test Locally**: Use `openclaw agent --message "use my new skill"` to test.
+ ```bash
+ openclaw skills list
+ ```
-## Shared Skills
+
-You can also browse and contribute skills to [ClawHub](https://clawhub.com).
+
+ Send a message that should trigger the skill:
+
+ ```bash
+ openclaw agent --message "give me a greeting"
+ ```
+
+ Or just chat with the agent and ask for a greeting.
+
+
+
+
+## Skill metadata reference
+
+The YAML frontmatter supports these fields:
+
+| Field | Required | Description |
+| ----------------------------------- | -------- | ------------------------------------------- |
+| `name` | Yes | Unique identifier (snake_case) |
+| `description` | Yes | One-line description shown to the agent |
+| `metadata.openclaw.os` | No | OS filter (`["darwin"]`, `["linux"]`, etc.) |
+| `metadata.openclaw.requires.bins` | No | Required binaries on PATH |
+| `metadata.openclaw.requires.config` | No | Required config keys |
+
+## Best practices
+
+- **Be concise** — instruct the model on _what_ to do, not how to be an AI
+- **Safety first** — if your skill uses `exec`, ensure prompts don't allow arbitrary command injection from untrusted input
+- **Test locally** — use `openclaw agent --message "..."` to test before sharing
+- **Use ClawHub** — browse and contribute skills at [ClawHub](https://clawhub.com)
+
+## Where skills live
+
+| Location | Precedence | Scope |
+| ------------------------------- | ---------- | --------------------- |
+| `\/skills/` | Highest | Per-agent |
+| `~/.openclaw/skills/` | Medium | Shared (all agents) |
+| Bundled (shipped with OpenClaw) | Lowest | Global |
+| `skills.load.extraDirs` | Lowest | Custom shared folders |
+
+## Related
+
+- [Skills reference](/tools/skills) — loading, precedence, and gating rules
+- [Skills config](/tools/skills-config) — `skills.*` config schema
+- [ClawHub](/tools/clawhub) — public skill registry
+- [Building Plugins](/plugins/building-plugins) — plugins can ship skills
diff --git a/docs/tools/elevated.md b/docs/tools/elevated.md
index c10b955ce2d..96a574f6fc9 100644
--- a/docs/tools/elevated.md
+++ b/docs/tools/elevated.md
@@ -1,63 +1,114 @@
---
-summary: "Elevated exec mode and /elevated directives"
+summary: "Elevated exec mode: run commands on the gateway host from a sandboxed agent"
read_when:
- Adjusting elevated mode defaults, allowlists, or slash command behavior
+ - Understanding how sandboxed agents can access the host
title: "Elevated Mode"
---
-# Elevated Mode (/elevated directives)
+# Elevated Mode
-## What it does
+When an agent runs inside a sandbox, its `exec` commands are confined to the
+sandbox environment. **Elevated mode** lets the agent break out and run commands
+on the gateway host instead, with configurable approval gates.
-- `/elevated on` runs on the gateway host and keeps exec approvals (same as `/elevated ask`).
-- `/elevated full` runs on the gateway host **and** auto-approves exec (skips exec approvals).
-- `/elevated ask` runs on the gateway host but keeps exec approvals (same as `/elevated on`).
-- `on`/`ask` do **not** force `exec.security=full`; configured security/ask policy still applies.
-- Only changes behavior when the agent is **sandboxed** (otherwise exec already runs on the host).
-- Directive forms: `/elevated on|off|ask|full`, `/elev on|off|ask|full`.
-- Only `on|off|ask|full` are accepted; anything else returns a hint and does not change state.
+
+ Elevated mode only changes behavior when the agent is **sandboxed**. For
+ unsandboxed agents, exec already runs on the host.
+
-## What it controls (and what it does not)
+## Directives
-- **Availability gates**: `tools.elevated` is the global baseline. `agents.list[].tools.elevated` can further restrict elevated per agent (both must allow).
-- **Per-session state**: `/elevated on|off|ask|full` sets the elevated level for the current session key.
-- **Inline directive**: `/elevated on|ask|full` inside a message applies to that message only.
-- **Groups**: In group chats, elevated directives are only honored when the agent is mentioned. Command-only messages that bypass mention requirements are treated as mentioned.
-- **Host execution**: elevated forces `exec` onto the gateway host; `full` also sets `security=full`.
-- **Approvals**: `full` skips exec approvals; `on`/`ask` honor them when allowlist/ask rules require.
-- **Unsandboxed agents**: no-op for location; only affects gating, logging, and status.
-- **Tool policy still applies**: if `exec` is denied by tool policy, elevated cannot be used.
-- **Separate from `/exec`**: `/exec` adjusts per-session defaults for authorized senders and does not require elevated.
+Control elevated mode per-session with slash commands:
+
+| Directive | What it does |
+| ---------------- | --------------------------------------------------- |
+| `/elevated on` | Run on the gateway host, keep exec approvals |
+| `/elevated ask` | Same as `on` (alias) |
+| `/elevated full` | Run on the gateway host **and** skip exec approvals |
+| `/elevated off` | Return to sandbox-confined execution |
+
+Also available as `/elev on|off|ask|full`.
+
+Send `/elevated` with no argument to see the current level.
+
+## How it works
+
+
+
+ Elevated must be enabled in config and the sender must be on the allowlist:
+
+ ```json5
+ {
+ tools: {
+ elevated: {
+ enabled: true,
+ allowFrom: {
+ discord: ["user-id-123"],
+ whatsapp: ["+15555550123"],
+ },
+ },
+ },
+ }
+ ```
+
+
+
+
+ Send a directive-only message to set the session default:
+
+ ```
+ /elevated full
+ ```
+
+ Or use it inline (applies to that message only):
+
+ ```
+ /elevated on run the deployment script
+ ```
+
+
+
+
+ With elevated active, `exec` calls route to the gateway host instead of the
+ sandbox. In `full` mode, exec approvals are skipped. In `on`/`ask` mode,
+ configured approval rules still apply.
+
+
## Resolution order
-1. Inline directive on the message (applies only to that message).
-2. Session override (set by sending a directive-only message).
-3. Global default (`agents.defaults.elevatedDefault` in config).
+1. **Inline directive** on the message (applies only to that message)
+2. **Session override** (set by sending a directive-only message)
+3. **Global default** (`agents.defaults.elevatedDefault` in config)
-## Setting a session default
+## Availability and allowlists
-- Send a message that is **only** the directive (whitespace allowed), e.g. `/elevated full`.
-- Confirmation reply is sent (`Elevated mode set to full...` / `Elevated mode disabled.`).
-- If elevated access is disabled or the sender is not on the approved allowlist, the directive replies with an actionable error and does not change session state.
-- Send `/elevated` (or `/elevated:`) with no argument to see the current elevated level.
+- **Global gate**: `tools.elevated.enabled` (must be `true`)
+- **Sender allowlist**: `tools.elevated.allowFrom` with per-channel lists
+- **Per-agent gate**: `agents.list[].tools.elevated.enabled` (can only further restrict)
+- **Per-agent allowlist**: `agents.list[].tools.elevated.allowFrom` (sender must match both global + per-agent)
+- **Discord fallback**: if `tools.elevated.allowFrom.discord` is omitted, `channels.discord.allowFrom` is used as fallback
+- **All gates must pass**; otherwise elevated is treated as unavailable
-## Availability + allowlists
+Allowlist entry formats:
-- Feature gate: `tools.elevated.enabled` (default can be off via config even if the code supports it).
-- Sender allowlist: `tools.elevated.allowFrom` with per-provider allowlists (e.g. `discord`, `whatsapp`).
-- Unprefixed allowlist entries match sender-scoped identity values only (`SenderId`, `SenderE164`, `From`); recipient routing fields are never used for elevated authorization.
-- Mutable sender metadata requires explicit prefixes:
- - `name:` matches `SenderName`
- - `username:` matches `SenderUsername`
- - `tag:` matches `SenderTag`
- - `id:`, `from:`, `e164:` are available for explicit identity targeting
-- Per-agent gate: `agents.list[].tools.elevated.enabled` (optional; can only further restrict).
-- Per-agent allowlist: `agents.list[].tools.elevated.allowFrom` (optional; when set, the sender must match **both** global + per-agent allowlists).
-- Discord fallback: if `tools.elevated.allowFrom.discord` is omitted, the `channels.discord.allowFrom` list is used as a fallback (legacy: `channels.discord.dm.allowFrom`). Set `tools.elevated.allowFrom.discord` (even `[]`) to override. Per-agent allowlists do **not** use the fallback.
-- All gates must pass; otherwise elevated is treated as unavailable.
+| Prefix | Matches |
+| ----------------------- | ------------------------------- |
+| (none) | Sender ID, E.164, or From field |
+| `name:` | Sender display name |
+| `username:` | Sender username |
+| `tag:` | Sender tag |
+| `id:`, `from:`, `e164:` | Explicit identity targeting |
-## Logging + status
+## What elevated does not control
-- Elevated exec calls are logged at info level.
-- Session status includes elevated mode (e.g. `elevated=ask`, `elevated=full`).
+- **Tool policy**: if `exec` is denied by tool policy, elevated cannot override it
+- **Separate from `/exec`**: the `/exec` directive adjusts per-session exec defaults for authorized senders and does not require elevated mode
+
+## Related
+
+- [Exec tool](/tools/exec) — shell command execution
+- [Exec approvals](/tools/exec-approvals) — approval and allowlist system
+- [Sandboxing](/gateway/sandboxing) — sandbox configuration
+- [Sandbox vs Tool Policy vs Elevated](/gateway/sandbox-vs-tool-policy-vs-elevated)
diff --git a/docs/tools/reactions.md b/docs/tools/reactions.md
index 17f9cfbb7f9..56d6b5942e7 100644
--- a/docs/tools/reactions.md
+++ b/docs/tools/reactions.md
@@ -1,23 +1,64 @@
---
-summary: "Reaction semantics shared across channels"
+summary: "Reaction tool semantics across all supported channels"
read_when:
- Working on reactions in any channel
+ - Understanding how emoji reactions differ across platforms
title: "Reactions"
---
-# Reaction tooling
+# Reactions
-Shared reaction semantics across channels:
+The agent can add and remove emoji reactions on messages using the `message`
+tool with the `react` action. Reaction behavior varies by channel.
+
+## How it works
+
+```json
+{
+ "action": "react",
+ "messageId": "msg-123",
+ "emoji": "thumbsup"
+}
+```
- `emoji` is required when adding a reaction.
-- `emoji=""` removes the bot's reaction(s) when supported.
-- `remove: true` removes the specified emoji when supported (requires `emoji`).
+- Set `emoji` to an empty string (`""`) to remove the bot's reaction(s).
+- Set `remove: true` to remove a specific emoji (requires non-empty `emoji`).
-Channel notes:
+## Channel behavior
-- **Discord/Slack**: empty `emoji` removes all of the bot's reactions on the message; `remove: true` removes just that emoji.
-- **Google Chat**: empty `emoji` removes the app's reactions on the message; `remove: true` removes just that emoji.
-- **Telegram**: empty `emoji` removes the bot's reactions; `remove: true` also removes reactions but still requires a non-empty `emoji` for tool validation.
-- **WhatsApp**: empty `emoji` removes the bot reaction; `remove: true` maps to empty emoji (still requires `emoji`).
-- **Zalo Personal (`zalouser`)**: requires non-empty `emoji`; `remove: true` removes that specific emoji reaction.
-- **Signal**: inbound reaction notifications emit system events when `channels.signal.reactionNotifications` is enabled.
+
+
+ - Empty `emoji` removes all of the bot's reactions on the message.
+ - `remove: true` removes just the specified emoji.
+
+
+
+ - Empty `emoji` removes the app's reactions on the message.
+ - `remove: true` removes just the specified emoji.
+
+
+
+ - Empty `emoji` removes the bot's reactions.
+ - `remove: true` also removes reactions but still requires a non-empty `emoji` for tool validation.
+
+
+
+ - Empty `emoji` removes the bot reaction.
+ - `remove: true` maps to empty emoji internally (still requires `emoji` in the tool call).
+
+
+
+ - Requires non-empty `emoji`.
+ - `remove: true` removes that specific emoji reaction.
+
+
+
+ - Inbound reaction notifications emit system events when `channels.signal.reactionNotifications` is enabled.
+
+
+
+## Related
+
+- [Agent Send](/tools/agent-send) — the `message` tool that includes `react`
+- [Channels](/channels) — channel-specific configuration