docs: refresh contributor docs

Co-authored-by: Quratulain-bilal <umayaimanshah@gmail.com>
Co-authored-by: Mariano Belinky <mbelinky@gmail.com>
Co-authored-by: tao <itaofe@gmail.com>
Co-authored-by: julian <julian@tencent.com>
Co-authored-by: xenouzik <xenouziq@gmail.com>
Co-authored-by: Olamiposi <56056759+posigit@users.noreply.github.com>
Co-authored-by: surlymochan <surlymo@apache.org>
Co-authored-by: Janaka A <contact@janaka.co.uk>
Co-authored-by: choiking <samsamuels1927@gmail.com>
This commit is contained in:
Peter Steinberger
2026-05-22 22:58:14 +01:00
parent 84f6b5c7f8
commit 64d13c017a
14 changed files with 320 additions and 6 deletions

View File

@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
### Changes
- Docs: clarify browser CDP diagnostics, Plugin SDK allowlist imports, status-reaction timing defaults, queue steering behavior, limited-tool troubleshooting, cron HEARTBEAT handling, Telegram multi-agent groups, Bitwarden SecretRef setup, and EasyRunner deployments. Thanks @Quratulain-bilal, @mbelinky, @Mickey-, @vancece, @xenouzik, @posigit, @surlymochan, @janaka, and @choiking.
- Docs: clarify IPv4-only Gateway BYOH binding, trusted-proxy scope clearing, Android pairing approval, macOS Accessibility grants, Zalo profile env vars, password-store SecretRef setup, and Chinese memory navigation. Thanks @itskai-dev, @gwh7078, @longstoryscott, @MoeJaberr, and @yuaiccc.
- Docs: consolidate GLM under Z.AI, add the Upstash Box install guide and Gateway exposure runbook, clarify MEDIA directives, Copilot and Voyage setup, config path quoting, real behavior proof, and memory-file write guidance. Thanks @BobDu, @alitariksahin, @Jefsky, @musaabhasan, @OmerZeyveli, @leno23, @WuKongAI-CMU, @luoyanglang, and @majin1102.
- Docs: clarify media provider credentials, Codex/OpenClaw code-mode boundaries, Slack and Telegram ack reactions, Feishu dynamic agents, secrets plaintext boundaries, memory guidance, and Chinese glossary terms. Thanks @nielskaspers, @cosmopolitan033, @drclaw-iq, @alexgduarte, @zccyman, @chengoak, and @cassthebandit.

View File

@@ -99,6 +99,13 @@ This fires ~56 times per month instead of 01 times per month. OpenClaw use
<AccordionGroup>
<Accordion title="Main session vs isolated vs custom">
**Main session** jobs enqueue a system event into a cron-owned run lane and optionally wake the heartbeat (`--wake now` or `--wake next-heartbeat`). They can use the target main session's last delivery context for replies, but they do not append routine cron turns to the human chat lane and do not extend daily/idle reset freshness for the target session. **Isolated** jobs run a dedicated agent turn with a fresh session. **Custom sessions** (`session:xxx`) persist context across runs, enabling workflows like daily standups that build on previous summaries.
Main-session cron events are self-contained system-event reminders. They do
not automatically include the default heartbeat prompt's "Read
HEARTBEAT.md" instruction. If a recurring reminder should consult
`HEARTBEAT.md`, say that explicitly in the cron event text or in the
agent's own instructions.
</Accordion>
<Accordion title="What 'fresh session' means for isolated jobs">
For isolated jobs, "fresh session" means a new transcript/session id for each run. OpenClaw may carry safe preferences such as thinking/fast/verbose settings, labels, and explicit user-selected model/auth overrides, but it does not inherit ambient conversation context from an older cron row: channel/group routing, send or queue policy, elevation, origin, or ACP runtime binding. Use `current` or `session:<id>` when a recurring job should deliberately build on the same conversation context.

View File

@@ -357,6 +357,11 @@ Common channels supporting this pattern include:
- Create one bot per agent with BotFather and copy each token.
- Tokens live in `channels.telegram.accounts.<id>.botToken` (default account can use `TELEGRAM_BOT_TOKEN`).
- For multiple bots in the same Telegram group, invite each bot and mention the bot that should answer.
- Disable BotFather Privacy Mode for each group bot, then re-add the bot so Telegram applies the setting.
- Allow groups with `channels.telegram.groups`, or use `groupPolicy: "open"` only for trusted group deployments.
- Put sender user IDs in `groupAllowFrom`. Group and supergroup IDs belong in `channels.telegram.groups`, not `groupAllowFrom`.
- Bind by `accountId` so each bot routes to its own agent.
</Accordion>
<Accordion title="WhatsApp numbers per agent">

View File

@@ -78,6 +78,20 @@ quiet window in `steer` mode:
Defaults: `debounceMs: 500`, `cap: 20`, `drop: summarize`.
## Steer and streaming
When channel streaming is `partial` or `block`, steering can look like several
short visible replies while the active run reaches runtime boundaries:
- `partial`: the preview may finalize early, then a new preview starts after
steering is accepted.
- `block`: draft-sized blocks can create the same sequential appearance.
- Without streaming, steering falls back to a followup after the active run when
the runtime cannot accept same-turn steering.
`steer` does not abort in-flight tools. Use `/queue interrupt` when the newest
message should abort the current run.
## Precedence
For mode selection, OpenClaw resolves:

View File

@@ -1437,7 +1437,8 @@
"platforms/linux",
"platforms/windows",
"platforms/android",
"platforms/ios"
"platforms/ios",
"platforms/easyrunner"
]
},
{

View File

@@ -311,6 +311,60 @@ the config fields that accept SecretRefs.
}
```
</Accordion>
<Accordion title="Bitwarden Secrets Manager (`bws`)">
Use a resolver wrapper when you want SecretRef ids to map to Bitwarden
Secrets Manager item keys. The repository includes
`scripts/secrets/openclaw-bws-resolver.mjs`; install or copy it to an absolute
trusted path on the host that runs the Gateway.
Requirements:
- Bitwarden Secrets Manager CLI (`bws`) installed on the Gateway host.
- `BWS_ACCESS_TOKEN` available to the Gateway service.
- `PATH` passed to the resolver, or `BWS_BIN` set to the absolute `bws`
binary path.
```json5
{
secrets: {
providers: {
bws: {
source: "exec",
command: "/usr/local/bin/openclaw-bws-resolver.mjs",
passEnv: ["BWS_ACCESS_TOKEN", "PATH", "BWS_BIN"],
jsonOnly: true,
},
},
},
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
models: [{ id: "gpt-5", name: "gpt-5" }],
apiKey: {
source: "exec",
provider: "bws",
id: "openclaw/providers/openai/apiKey",
},
},
},
},
}
```
The resolver batches requested ids, runs `bws secret list`, and returns
values for matching secret `key` fields. Use keys that satisfy the exec
SecretRef id contract, such as `openclaw/providers/openai/apiKey`; env-var
style keys with underscores are rejected before the resolver runs. If more
than one visible Bitwarden secret has the same requested key, the resolver
fails that id as ambiguous instead of choosing one. After updating config,
verify the resolver path:
```bash
openclaw secrets audit --allow-exec
```
</Accordion>
<Accordion title="HashiCorp Vault CLI">
```json5
{

View File

@@ -34,6 +34,31 @@ Good output in one line:
gateway is unreachable, the command falls back to config-only summaries.
- `openclaw logs --follow` → steady activity, no repeating fatal errors.
## Assistant feels limited or missing tools
If the assistant cannot inspect files, run commands, use browser automation, or
see expected tools, check the effective tool profile first:
```bash
openclaw status
openclaw status --all
openclaw doctor
```
Common causes:
- `tools.profile: "messaging"` is intentionally narrow for chat-only agents.
- `tools.profile: "coding"` is the usual profile for repository, file, shell,
and runtime workflows.
- `tools.profile: "full"` exposes the broadest tool set and should be limited
to trusted operator-controlled agents.
- Per-agent `agents.list[].tools` overrides can narrow or expand the root
profile for one agent.
Change the root or per-agent tool profile, then restart or reload the Gateway
and run `openclaw status --all` again. See [Tools](/tools) for the profile
model and allow/deny overrides.
## Anthropic long context 429
If you see:

View File

@@ -0,0 +1,109 @@
---
summary: "Run the OpenClaw Gateway on EasyRunner with Podman and Caddy"
read_when:
- Deploying OpenClaw on EasyRunner
- Running the Gateway behind EasyRunner's Caddy proxy
- Choosing persistent volumes and auth for a hosted Gateway
title: "EasyRunner"
---
EasyRunner can host the OpenClaw Gateway as a small containerized app behind its
Caddy proxy. This guide assumes an EasyRunner host that runs Podman-compatible
Compose apps and exposes HTTPS through Caddy.
## Before you begin
- An EasyRunner server with a domain routed to it.
- A built or published OpenClaw container image.
- A persistent config volume for `/home/node/.openclaw`.
- A persistent workspace volume for `/workspace`.
- A strong Gateway token or password.
Keep device auth enabled when possible. If your reverse proxy deployment cannot
carry device identity correctly, fix trusted-proxy settings first; use
dangerous auth bypasses only for a fully private, operator-controlled network.
## Compose app
Create an EasyRunner app with a Compose file shaped like this:
```yaml
services:
openclaw:
image: ghcr.io/openclaw/openclaw:latest
restart: unless-stopped
environment:
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN}
OPENCLAW_HOME: /home/node
OPENCLAW_STATE_DIR: /home/node/.openclaw
OPENCLAW_CONFIG_PATH: /home/node/.openclaw/openclaw.json
OPENCLAW_WORKSPACE_DIR: /workspace
volumes:
- openclaw-config:/home/node/.openclaw
- openclaw-workspace:/workspace
labels:
caddy: openclaw.example.com
caddy.reverse_proxy: "{{upstreams 1455}}"
command: ["openclaw", "gateway", "--bind", "lan", "--port", "1455"]
volumes:
openclaw-config:
openclaw-workspace:
```
Replace `openclaw.example.com` with your Gateway hostname. Store
`OPENCLAW_GATEWAY_TOKEN` in EasyRunner's secret/environment manager instead of
committing it to the app definition.
## Configure OpenClaw
Inside the persistent config volume, keep the Gateway reachable only through
the proxy and require auth:
```json5
{
gateway: {
bind: "lan",
port: 1455,
auth: {
token: "${OPENCLAW_GATEWAY_TOKEN}",
},
},
}
```
If Caddy terminates TLS for the Gateway, configure trusted proxy settings for
the exact proxy path rather than disabling auth checks globally. See
[Trusted proxy auth](/gateway/trusted-proxy-auth).
## Verify
From your workstation:
```bash
openclaw gateway probe --url https://openclaw.example.com --token <token>
openclaw gateway status --url https://openclaw.example.com --token <token>
```
From the EasyRunner host, check the app logs for a listening Gateway and no
startup SecretRef, plugin, or channel auth failures.
## Updates and backups
- Pull or build the new OpenClaw image, then redeploy the EasyRunner app.
- Back up the `openclaw-config` volume before updates.
- Back up `openclaw-workspace` if agents write durable project data there.
- Run `openclaw doctor` after major updates to catch config migrations and
service warnings.
## Troubleshooting
- `gateway probe` cannot connect: confirm the Caddy hostname points at the app
and that the container listens on `0.0.0.0:1455`.
- Auth fails: rotate the token in EasyRunner secrets and the local client
command together.
- Files are root-owned after restore: repair the mounted volumes so the
container user can write `/home/node/.openclaw` and `/workspace`.
- Browser or channel plugins fail: check whether the required external
binaries, network egress, and mounted credentials are available inside the
container.

View File

@@ -30,6 +30,7 @@ Native companion apps for Windows are also planned; the Gateway is recommended v
- GCP (Compute Engine): [GCP](/install/gcp)
- Azure (Linux VM): [Azure](/install/azure)
- exe.dev (VM + HTTPS proxy): [exe.dev](/install/exe-dev)
- EasyRunner (Podman + Caddy): [EasyRunner](/platforms/easyrunner)
## Common links

View File

@@ -563,8 +563,7 @@ releases.
| `plugin-sdk/fetch-runtime` | Wrapped fetch/proxy helpers | `resolveFetch`, proxy helpers, EnvHttpProxyAgent option helpers |
| `plugin-sdk/host-runtime` | Host normalization helpers | `normalizeHostname`, `normalizeScpRemoteHost` |
| `plugin-sdk/retry-runtime` | Retry helpers | `RetryConfig`, `retryAsync`, policy runners |
| `plugin-sdk/allow-from` | Allowlist formatting | `formatAllowFromLowercase` |
| `plugin-sdk/allowlist-resolution` | Allowlist input mapping | `mapAllowlistResolutionInputs` |
| `plugin-sdk/allow-from` | Allowlist formatting and input mapping | `formatAllowFromLowercase`, `mapAllowlistResolutionInputs` |
| `plugin-sdk/command-auth` | Command gating and command-surface helpers | `resolveControlCommandGate`, sender-authorization helpers, command registry helpers including dynamic argument menu formatting |
| `plugin-sdk/command-status` | Command status/help renderers | `buildCommandsMessage`, `buildCommandsMessagePaginated`, `buildHelpMessage` |
| `plugin-sdk/secret-input` | Secret input parsing | Secret input helpers |

View File

@@ -442,6 +442,10 @@ CDP URL shapes and picks the right connection strategy automatically:
providers can still use their root WebSocket endpoint when their discovery
endpoint advertises a short-lived URL that is not suitable for Playwright CDP.
`openclaw browser doctor` uses the same discovery-first, WebSocket-fallback
logic as runtime attach, so a bare-root URL that connects successfully is not
reported as unreachable by diagnostics.
### Browserbase
[Browserbase](https://www.browserbase.com) is a cloud platform for running

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env node
import { execFileSync } from "node:child_process";
const readStdin = () =>
new Promise((resolve, reject) => {
let input = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => {
input += chunk;
});
process.stdin.on("end", () => resolve(input));
process.stdin.on("error", reject);
});
const parseRequest = (input) => {
try {
return JSON.parse(input || "{}");
} catch (error) {
throw new Error(`Failed to parse request JSON: ${error.message}`);
}
};
const isSecretRecord = (value) =>
value &&
typeof value === "object" &&
typeof value.key === "string" &&
typeof value.value === "string";
const main = async () => {
const request = parseRequest(await readStdin());
if (request.protocolVersion !== 1) {
throw new Error("Unsupported SecretRef protocolVersion");
}
const ids = Array.isArray(request.ids)
? request.ids.filter((id) => typeof id === "string" && id.length > 0)
: [];
if (ids.length === 0) {
process.stdout.write(JSON.stringify({ protocolVersion: 1, values: {}, errors: {} }));
return;
}
if (!process.env.BWS_ACCESS_TOKEN) {
throw new Error("BWS_ACCESS_TOKEN is required");
}
const bwsBin =
process.env.BWS_BIN && process.env.BWS_BIN.trim() ? process.env.BWS_BIN.trim() : "bws";
const raw = execFileSync(bwsBin, ["secret", "list"], {
encoding: "utf8",
env: {
BWS_ACCESS_TOKEN: process.env.BWS_ACCESS_TOKEN,
PATH: process.env.PATH || "",
},
maxBuffer: 1024 * 1024,
timeout: 15_000,
});
let secrets;
try {
secrets = JSON.parse(raw);
} catch (error) {
throw new Error(`Failed to parse bws output: ${error.message}`);
}
const byKey = new Map();
for (const secret of Array.isArray(secrets) ? secrets : []) {
if (isSecretRecord(secret)) {
const values = byKey.get(secret.key) || [];
values.push(secret.value);
byKey.set(secret.key, values);
}
}
const values = {};
const errors = {};
for (const id of ids) {
const matches = byKey.get(id) || [];
if (matches.length === 1) {
values[id] = matches[0];
} else if (matches.length > 1) {
errors[id] = { message: "ambiguous duplicate key" };
} else {
errors[id] = { message: "not found" };
}
}
process.stdout.write(JSON.stringify({ protocolVersion: 1, values, errors }));
};
main().catch((error) => {
process.stderr.write(`${error.message}\n`);
process.exit(1);
});

View File

@@ -1918,7 +1918,7 @@ export const FIELD_HELP: Record<string, string> = {
"messages.statusReactions.emojis":
"Override default status reaction emojis. Keys: queued, thinking, compacting, tool, coding, web, deploy, build, concierge, done, error, stallSoft, stallHard. Telegram chooses the first supported fallback when a configured emoji is not available in the chat.",
"messages.statusReactions.timing":
"Override default timing. Keys: debounceMs (700), stallSoftMs (25000), stallHardMs (60000), doneHoldMs (1500), errorHoldMs (2500).",
"Override default timing. Keys: debounceMs (700), stallSoftMs (10000), stallHardMs (30000), doneHoldMs (1500), errorHoldMs (2500).",
"messages.inbound.debounceMs":
"Debounce window (ms) for batching rapid inbound messages from the same sender (0 to disable).",
};

View File

@@ -79,9 +79,9 @@ export type StatusReactionsEmojiConfig = {
export type StatusReactionsTimingConfig = {
/** Debounce interval for intermediate states (ms). Default: 700. */
debounceMs?: number;
/** Soft stall warning timeout (ms). Default: 25000. */
/** Soft stall warning timeout (ms). Default: 10000. */
stallSoftMs?: number;
/** Hard stall warning timeout (ms). Default: 60000. */
/** Hard stall warning timeout (ms). Default: 30000. */
stallHardMs?: number;
/** How long to hold done emoji before cleanup (ms). Default: 1500. */
doneHoldMs?: number;