docs: rename onboarding user-facing wizard copy

Co-authored-by: Tak <contact-redacted@example.com>
This commit is contained in:
Tak Hoffman
2026-03-16 19:50:31 -05:00
parent 6ba4d0ddc3
commit 4863b651c6
64 changed files with 235 additions and 780 deletions

View File

@@ -105,7 +105,6 @@ Docs: https://docs.openclaw.ai
- Agents/usage tracking: stop forcing `supportsUsageInStreaming: false` on non-native OpenAI-completions providers so compatible backends report token usage and cost again instead of showing all zeros. (#46500) Fixes #46142. Thanks @ademczuk.
- Plugins/subagents: preserve gateway-owned plugin subagent access across runtime, tool, and embedded-runner load paths so gateway plugin tools and context engines can still spawn and manage subagents after the loader cache split. (#46648) Thanks @jalehman.
- Control UI/overview: keep the language dropdown aligned with the persisted locale during dashboard startup so refreshing the page does not fall back to English before locale hydration completes. (#48019) Thanks @git-jxj.
- Agents/compaction: rerun transcript repair after `session.compact()` so orphaned `tool_result` blocks cannot survive compaction and break later Anthropic requests. (#16095) thanks @claw-sylphx.
## 2026.3.13
@@ -184,7 +183,6 @@ Docs: https://docs.openclaw.ai
- Auth/login lockout recovery: clear stale `auth_permanent` and `billing` disabled state for all profiles matching the target provider when `openclaw models auth login` is invoked, so users locked out by expired or revoked OAuth tokens can recover by re-authenticating instead of waiting for the cooldown timer to expire. (#43057)
- Auto-reply/context-engine compaction: persist the exact embedded-run metadata compaction count for main and followup runner session accounting, so metadata-only auto-compactions no longer undercount multi-compaction runs. (#42629) thanks @uf-hy.
- Auth/Codex CLI reuse: sync reused Codex CLI credentials into the supported `openai-codex:default` OAuth profile instead of reviving the deprecated `openai-codex:codex-cli` slot, so doctor cleanup no longer loops. (#45353) thanks @Gugu-sugar.
- WhatsApp/group replies: recognize implicit reply-to-bot mentions when WhatsApp sends the quoted sender in `@lid` format, including device-suffixed self identities. (#23029) Thanks @sparkyrider.
## 2026.3.12
@@ -276,7 +274,6 @@ Docs: https://docs.openclaw.ai
- Agents/Anthropic replay: drop replayed assistant thinking blocks for native Anthropic and Bedrock Claude providers so persisted follow-up turns no longer fail on stored thinking blocks. (#44843) Thanks @jmcte.
- Docs/Brave pricing: escape literal dollar signs in Brave Search cost text so the docs render the free credit and per-request pricing correctly. (#44989) Thanks @keelanfh.
- Feishu/file uploads: preserve literal UTF-8 filenames in `im.file.create` so Chinese and other non-ASCII filenames no longer appear percent-encoded in chat. (#34262) Thanks @fabiaodemianyang and @KangShuaiFu.
- Agents/compaction safeguard: trim large kept `toolResult` payloads consistently for budgeting, pruning, and identifier seeding, then restore preserved payloads after prune so oversized safeguard summaries stay stable. (#44133) thanks @SayrWolfridge.
## 2026.3.11

View File

@@ -23,10 +23,10 @@ It answers you on the channels you already use (WhatsApp, Telegram, Slack, Disco
If you want a personal, single-user assistant that feels local, fast, and always-on, this is it.
[Website](https://openclaw.ai) · [Docs](https://docs.openclaw.ai) · [Vision](VISION.md) · [DeepWiki](https://deepwiki.com/openclaw/openclaw) · [Getting Started](https://docs.openclaw.ai/start/getting-started) · [Updating](https://docs.openclaw.ai/install/updating) · [Showcase](https://docs.openclaw.ai/start/showcase) · [FAQ](https://docs.openclaw.ai/help/faq) · [Wizard](https://docs.openclaw.ai/start/wizard) · [Nix](https://github.com/openclaw/nix-openclaw) · [Docker](https://docs.openclaw.ai/install/docker) · [Discord](https://discord.gg/clawd)
[Website](https://openclaw.ai) · [Docs](https://docs.openclaw.ai) · [Vision](VISION.md) · [DeepWiki](https://deepwiki.com/openclaw/openclaw) · [Getting Started](https://docs.openclaw.ai/start/getting-started) · [Updating](https://docs.openclaw.ai/install/updating) · [Showcase](https://docs.openclaw.ai/start/showcase) · [FAQ](https://docs.openclaw.ai/help/faq) · [Onboarding](https://docs.openclaw.ai/start/wizard) · [Nix](https://github.com/openclaw/nix-openclaw) · [Docker](https://docs.openclaw.ai/install/docker) · [Discord](https://discord.gg/clawd)
Preferred setup: run the onboarding wizard (`openclaw onboard`) in your terminal.
The wizard guides you step by step through setting up the gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**.
Preferred setup: run `openclaw onboard` in your terminal.
OpenClaw Onboard guides you step by step through setting up the gateway, workspace, channels, and skills. It is the recommended CLI setup path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**.
Works with npm, pnpm, or bun.
New install? Start here: [Getting started](https://docs.openclaw.ai/start/getting-started)
@@ -58,7 +58,7 @@ npm install -g openclaw@latest
openclaw onboard --install-daemon
```
The wizard installs the Gateway daemon (launchd/systemd user service) so it stays running.
OpenClaw Onboard installs the Gateway daemon (launchd/systemd user service) so it stays running.
## Quick start (TL;DR)
@@ -132,7 +132,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
- **[Live Canvas](https://docs.openclaw.ai/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui).
- **[First-class tools](https://docs.openclaw.ai/tools)** — browser, canvas, nodes, cron, sessions, and Discord/Slack actions.
- **[Companion apps](https://docs.openclaw.ai/platforms/macos)** — macOS menu bar app + iOS/Android [nodes](https://docs.openclaw.ai/nodes).
- **[Onboarding](https://docs.openclaw.ai/start/wizard) + [skills](https://docs.openclaw.ai/tools/skills)** — wizard-driven setup with bundled/managed/workspace skills.
- **[Onboarding](https://docs.openclaw.ai/start/wizard) + [skills](https://docs.openclaw.ai/tools/skills)** — onboarding-driven setup with bundled/managed/workspace skills.
## Star History
@@ -143,7 +143,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
### Core platform
- [Gateway WS control plane](https://docs.openclaw.ai/gateway) with sessions, presence, config, cron, webhooks, [Control UI](https://docs.openclaw.ai/web), and [Canvas host](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui).
- [CLI surface](https://docs.openclaw.ai/tools/agent-send): gateway, agent, send, [wizard](https://docs.openclaw.ai/start/wizard), and [doctor](https://docs.openclaw.ai/gateway/doctor).
- [CLI surface](https://docs.openclaw.ai/tools/agent-send): gateway, agent, send, [onboarding](https://docs.openclaw.ai/start/wizard), and [doctor](https://docs.openclaw.ai/gateway/doctor).
- [Pi agent runtime](https://docs.openclaw.ai/concepts/agent) in RPC mode with tool streaming and block streaming.
- [Session model](https://docs.openclaw.ai/concepts/session): `main` for direct chats, group isolation, activation modes, queue modes, reply-back. Group rules: [Groups](https://docs.openclaw.ai/channels/groups).
- [Media pipeline](https://docs.openclaw.ai/nodes/images): images/audio/video, transcription hooks, size caps, temp file lifecycle. Audio details: [Audio](https://docs.openclaw.ai/nodes/audio).
@@ -422,7 +422,7 @@ Use these when youre past the onboarding flow and want the deeper reference.
- [Run the Gateway by the book with the operational runbook.](https://docs.openclaw.ai/gateway)
- [Learn how the Control UI/Web surfaces work and how to expose them safely.](https://docs.openclaw.ai/web)
- [Understand remote access over SSH tunnels or tailnets.](https://docs.openclaw.ai/gateway/remote)
- [Follow the onboarding wizard flow for a guided setup.](https://docs.openclaw.ai/start/wizard)
- [Follow OpenClaw Onboard for a guided setup.](https://docs.openclaw.ai/start/wizard)
- [Wire external triggers via the webhook surface.](https://docs.openclaw.ai/automation/webhook)
- [Set up Gmail Pub/Sub triggers.](https://docs.openclaw.ai/automation/gmail-pubsub)
- [Learn the macOS menu bar companion details.](https://docs.openclaw.ai/platforms/mac/menu-bar)

View File

@@ -126,7 +126,7 @@ launchctl load ~/Library/LaunchAgents/com.user.poke-messages.plist
## Onboarding
BlueBubbles is available in the interactive setup wizard:
BlueBubbles is available in interactive onboarding:
```
openclaw onboard

View File

@@ -30,9 +30,9 @@ openclaw plugins install @openclaw/feishu
There are two ways to add the Feishu channel:
### Method 1: setup wizard (recommended)
### Method 1: onboarding (recommended)
If you just installed OpenClaw, run the setup wizard:
If you just installed OpenClaw, run onboarding:
```bash
openclaw onboard

View File

@@ -16,7 +16,7 @@ Nostr is a decentralized protocol for social networking. This channel enables Op
### Onboarding (recommended)
- The setup wizard (`openclaw onboard`) and `openclaw channels add` list optional channel plugins.
- Onboarding (`openclaw onboard`) and `openclaw channels add` list optional channel plugins.
- Selecting Nostr prompts you to install the plugin on demand.
Install defaults:

View File

@@ -115,7 +115,7 @@ Token resolution order is account-aware. In practice, config values win over env
`channels.telegram.allowFrom` accepts numeric Telegram user IDs. `telegram:` / `tg:` prefixes are accepted and normalized.
`dmPolicy: "allowlist"` with empty `allowFrom` blocks all DMs and is rejected by config validation.
The setup wizard accepts `@username` input and resolves it to numeric IDs.
Onboarding accepts `@username` input and resolves it to numeric IDs.
If you upgraded and your config contains `@username` allowlist entries, run `openclaw doctor --fix` to resolve them (best-effort; requires a Telegram bot token).
If you previously relied on pairing-store allowlist files, `openclaw doctor --fix` can recover entries into `channels.telegram.allowFrom` in allowlist flows (for example when `dmPolicy: "allowlist"` has no explicit IDs yet).

View File

@@ -318,22 +318,22 @@ Initialize config + workspace.
Options:
- `--workspace <dir>`: agent workspace path (default `~/.openclaw/workspace`).
- `--wizard`: run the setup wizard.
- `--non-interactive`: run wizard without prompts.
- `--mode <local|remote>`: wizard mode.
- `--wizard`: run onboarding.
- `--non-interactive`: run onboarding without prompts.
- `--mode <local|remote>`: onboard mode.
- `--remote-url <url>`: remote Gateway URL.
- `--remote-token <token>`: remote Gateway token.
Wizard auto-runs when any wizard flags are present (`--non-interactive`, `--mode`, `--remote-url`, `--remote-token`).
Onboarding auto-runs when any onboarding flags are present (`--non-interactive`, `--mode`, `--remote-url`, `--remote-token`).
### `onboard`
Interactive wizard to set up gateway, workspace, and skills.
Interactive onboarding for gateway, workspace, and skills.
Options:
- `--workspace <dir>`
- `--reset` (reset config + credentials + sessions before wizard)
- `--reset` (reset config + credentials + sessions before onboarding)
- `--reset-scope <config|config+creds+sessions|full>` (default `config+creds+sessions`; use `full` to also remove workspace)
- `--non-interactive`
- `--mode <local|remote>`

View File

@@ -1,5 +1,5 @@
---
summary: "CLI reference for `openclaw onboard` (interactive setup wizard)"
summary: "CLI reference for `openclaw onboard` (interactive onboarding)"
read_when:
- You want guided setup for gateway, workspace, auth, channels, and skills
title: "onboard"
@@ -7,11 +7,11 @@ title: "onboard"
# `openclaw onboard`
Interactive setup wizard (local or remote Gateway setup).
Interactive onboarding for local or remote Gateway setup.
## Related guides
- CLI onboarding hub: [Setup Wizard (CLI)](/start/wizard)
- CLI onboarding hub: [Onboarding (CLI)](/start/wizard)
- Onboarding overview: [Onboarding Overview](/start/onboarding-overview)
- CLI onboarding reference: [CLI Setup Reference](/start/wizard-cli-reference)
- CLI automation: [CLI Automation](/start/wizard-cli-automation)

View File

@@ -1,7 +1,7 @@
---
summary: "CLI reference for `openclaw setup` (initialize config + workspace)"
read_when:
- Youre doing first-run setup without the full setup wizard
- Youre doing first-run setup without full CLI onboarding
- You want to set the default workspace path
title: "setup"
---
@@ -13,7 +13,7 @@ Initialize `~/.openclaw/openclaw.json` and the agent workspace.
Related:
- Getting started: [Getting started](/start/getting-started)
- Wizard: [Onboarding](/start/onboarding)
- CLI onboarding: [Onboarding (CLI)](/start/wizard)
## Examples
@@ -22,7 +22,7 @@ openclaw setup
openclaw setup --workspace ~/.openclaw/workspace
```
To run the wizard via setup:
To run onboarding via setup:
```bash
openclaw setup --wizard

View File

@@ -34,9 +34,9 @@ Related:
- Use fallbacks for cost/latency-sensitive tasks and lower-stakes chat.
- For tool-enabled agents or untrusted inputs, avoid older/weaker model tiers.
## Setup wizard (recommended)
## Onboarding (recommended)
If you dont want to hand-edit config, run the setup wizard:
If you dont want to hand-edit config, run onboarding:
```bash
openclaw onboard

View File

@@ -49,7 +49,7 @@ openclaw models status
openclaw doctor
```
If youd rather not manage env vars yourself, the setup wizard can store
If youd rather not manage env vars yourself, onboarding can store
API keys for daemon use: `openclaw onboard`.
See [Help](/help) for details on env inheritance (`env.shellEnv`,

View File

@@ -2950,7 +2950,7 @@ Notes:
## Wizard
Metadata written by CLI wizards (`onboard`, `configure`, `doctor`):
Metadata written by CLI guided setup flows (`onboard`, `configure`, `doctor`):
```json5
{

View File

@@ -38,7 +38,7 @@ See the [full reference](/gateway/configuration-reference) for every available f
<Tabs>
<Tab title="Interactive wizard">
```bash
openclaw onboard # full setup wizard
openclaw onboard # full onboarding flow
openclaw configure # config wizard
```
</Tab>

View File

@@ -738,7 +738,7 @@ In minimal mode, the Gateway still broadcasts enough for device discovery (`role
Gateway auth is **required by default**. If no token/password is configured,
the Gateway refuses WebSocket connections (failclosed).
The setup wizard generates a token by default (even for loopback) so
Onboarding generates a token by default (even for loopback) so
local clients must authenticate.
Set a token so **all** WS clients must authenticate:

View File

@@ -36,7 +36,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
- [How do I install OpenClaw on a VPS?](#how-do-i-install-openclaw-on-a-vps)
- [Where are the cloud/VPS install guides?](#where-are-the-cloudvps-install-guides)
- [Can I ask OpenClaw to update itself?](#can-i-ask-openclaw-to-update-itself)
- [What does the setup wizard actually do?](#what-does-the-setup-wizard-actually-do)
- [What does onboarding actually do?](#what-does-onboarding-actually-do)
- [Do I need a Claude or OpenAI subscription to run this?](#do-i-need-a-claude-or-openai-subscription-to-run-this)
- [Can I use Claude Max subscription without an API key](#can-i-use-claude-max-subscription-without-an-api-key)
- [How does Anthropic "setup-token" auth work?](#how-does-anthropic-setuptoken-auth-work)
@@ -317,7 +317,7 @@ Install docs: [Install](/install), [Installer flags](/install/installer), [Updat
### What's the recommended way to install and set up OpenClaw
The repo recommends running from source and using the setup wizard:
The repo recommends running from source and using onboarding:
```bash
curl -fsSL https://openclaw.ai/install.sh | bash
@@ -627,7 +627,7 @@ More detail: [Install](/install) and [Installer flags](/install/installer).
### How do I install OpenClaw on Linux
Short answer: follow the Linux guide, then run the setup wizard.
Short answer: follow the Linux guide, then run onboarding.
- Linux quick path + service install: [Linux](/platforms/linux).
- Full walkthrough: [Getting Started](/start/getting-started).
@@ -685,7 +685,7 @@ openclaw gateway restart
Docs: [Update](/cli/update), [Updating](/install/updating).
### What does the setup wizard actually do
### What does onboarding actually do
`openclaw onboard` is the recommended setup path. In **local mode** it walks you through:
@@ -723,7 +723,7 @@ If you want the clearest and safest supported path for production, use an Anthro
### How does Anthropic setuptoken auth work
`claude setup-token` generates a **token string** via the Claude Code CLI (it is not available in the web console). You can run it on **any machine**. Choose **Anthropic token (paste setup-token)** in the wizard or paste it with `openclaw models auth paste-token --provider anthropic`. The token is stored as an auth profile for the **anthropic** provider and used like an API key (no auto-refresh). More detail: [OAuth](/concepts/oauth).
`claude setup-token` generates a **token string** via the Claude Code CLI (it is not available in the web console). You can run it on **any machine**. Choose **Anthropic token (paste setup-token)** in onboarding or paste it with `openclaw models auth paste-token --provider anthropic`. The token is stored as an auth profile for the **anthropic** provider and used like an API key (no auto-refresh). More detail: [OAuth](/concepts/oauth).
### Where do I find an Anthropic setuptoken
@@ -733,7 +733,7 @@ It is **not** in the Anthropic Console. The setup-token is generated by the **Cl
claude setup-token
```
Copy the token it prints, then choose **Anthropic token (paste setup-token)** in the wizard. If you want to run it on the gateway host, use `openclaw models auth setup-token --provider anthropic`. If you ran `claude setup-token` elsewhere, paste it on the gateway host with `openclaw models auth paste-token --provider anthropic`. See [Anthropic](/providers/anthropic).
Copy the token it prints, then choose **Anthropic token (paste setup-token)** in onboarding. If you want to run it on the gateway host, use `openclaw models auth setup-token --provider anthropic`. If you ran `claude setup-token` elsewhere, paste it on the gateway host with `openclaw models auth paste-token --provider anthropic`. See [Anthropic](/providers/anthropic).
### Do you support Claude subscription auth (Claude Pro or Max)
@@ -767,15 +767,15 @@ Yes - via pi-ai's **Amazon Bedrock (Converse)** provider with **manual config**.
### How does Codex auth work
OpenClaw supports **OpenAI Code (Codex)** via OAuth (ChatGPT sign-in). The wizard can run the OAuth flow and will set the default model to `openai-codex/gpt-5.4` when appropriate. See [Model providers](/concepts/model-providers) and [Wizard](/start/wizard).
OpenClaw supports **OpenAI Code (Codex)** via OAuth (ChatGPT sign-in). Onboarding can run the OAuth flow and will set the default model to `openai-codex/gpt-5.4` when appropriate. See [Model providers](/concepts/model-providers) and [Onboarding (CLI)](/start/wizard).
### Do you support OpenAI subscription auth Codex OAuth
Yes. OpenClaw fully supports **OpenAI Code (Codex) subscription OAuth**.
OpenAI explicitly allows subscription OAuth usage in external tools/workflows
like OpenClaw. The setup wizard can run the OAuth flow for you.
like OpenClaw. Onboarding can run the OAuth flow for you.
See [OAuth](/concepts/oauth), [Model providers](/concepts/model-providers), and [Wizard](/start/wizard).
See [OAuth](/concepts/oauth), [Model providers](/concepts/model-providers), and [Onboarding (CLI)](/start/wizard).
### How do I set up Gemini CLI OAuth
@@ -844,7 +844,7 @@ without WhatsApp/Telegram.
`channels.telegram.allowFrom` is **the human sender's Telegram user ID** (numeric). It is not the bot username.
The setup wizard accepts `@username` input and resolves it to a numeric ID, but OpenClaw authorization uses numeric IDs only.
Onboarding accepts `@username` input and resolves it to a numeric ID, but OpenClaw authorization uses numeric IDs only.
Safer (no third-party bot):
@@ -1909,7 +1909,7 @@ openclaw onboard --install-daemon
Notes:
- The setup wizard also offers **Reset** if it sees an existing config. See [Wizard](/start/wizard).
- Onboarding also offers **Reset** if it sees an existing config. See [Onboarding (CLI)](/start/wizard).
- If you used profiles (`--profile` / `OPENCLAW_PROFILE`), reset each state dir (defaults are `~/.openclaw-<profile>`).
- Dev reset: `openclaw gateway --dev --reset` (dev-only; wipes dev config + credentials + sessions + workspace).

View File

@@ -33,7 +33,7 @@ title: "OpenClaw"
<Card title="Get Started" href="/start/getting-started" icon="rocket">
Install OpenClaw and bring up the Gateway in minutes.
</Card>
<Card title="Run the Wizard" href="/start/wizard" icon="sparkles">
<Card title="Run Onboarding" href="/start/wizard" icon="sparkles">
Guided setup with `openclaw onboard` and pairing flows.
</Card>
<Card title="Open the Control UI" href="/web/control-ui" icon="layout-dashboard">

View File

@@ -51,7 +51,7 @@ From repo root:
This script:
- builds the gateway image locally (or pulls a remote image if `OPENCLAW_IMAGE` is set)
- runs the setup wizard
- runs onboarding
- prints optional provider setup hints
- starts the gateway via Docker Compose
- generates a gateway token and writes it to `.env`

View File

@@ -33,7 +33,7 @@ For VPS/cloud hosts, avoid third-party "1-click" marketplace images when possibl
<AccordionGroup>
<Accordion title="Installer script" icon="rocket" defaultOpen>
Downloads the CLI, installs it globally via npm, and launches the setup wizard.
Downloads the CLI, installs it globally via npm, and launches onboarding.
<Tabs>
<Tab title="macOS / Linux / WSL2">

View File

@@ -21,7 +21,7 @@ and you configure everything via the `/setup` web wizard.
## What you get
- Hosted OpenClaw Gateway + Control UI
- Web setup wizard at `/setup` (no terminal commands)
- Web setup at `/setup` (no terminal commands)
- Persistent storage via Northflank Volume (`/data`) so config/credentials/workspace survive redeploys
## Setup flow
@@ -32,7 +32,7 @@ and you configure everything via the `/setup` web wizard.
4. Click **Run setup**.
5. Open the Control UI at `https://<your-northflank-domain>/openclaw`
If Telegram DMs are set to pairing, the setup wizard can approve the pairing code.
If Telegram DMs are set to pairing, web setup can approve the pairing code.
## Getting chat tokens

View File

@@ -29,13 +29,13 @@ Railway will either:
Then open:
- `https://<your-railway-domain>/setup` — setup wizard (password protected)
- `https://<your-railway-domain>/setup` — web setup (password protected)
- `https://<your-railway-domain>/openclaw` — Control UI
## What you get
- Hosted OpenClaw Gateway + Control UI
- Web setup wizard at `/setup` (no terminal commands)
- Web setup at `/setup` (no terminal commands)
- Persistent storage via Railway Volume (`/data`) so config/credentials/workspace survive redeploys
- Backup export at `/setup/export` to migrate off Railway later
@@ -70,7 +70,7 @@ Set these variables on the service:
3. (Optional) Add Telegram/Discord/Slack tokens.
4. Click **Run setup**.
If Telegram DMs are set to pairing, the setup wizard can approve the pairing code.
If Telegram DMs are set to pairing, web setup can approve the pairing code.
## Getting chat tokens

View File

@@ -73,7 +73,7 @@ The Blueprint defaults to `starter`. To use free tier, change `plan: free` in yo
## After deployment
### Complete the setup wizard
### Complete web setup
1. Navigate to `https://<your-service>.onrender.com/setup`
2. Enter your `SETUP_PASSWORD`

View File

@@ -22,7 +22,7 @@ curl -fsSL https://openclaw.ai/install.sh | bash
Notes:
- Add `--no-onboard` if you dont want the setup wizard to run again.
- Add `--no-onboard` if you dont want onboarding to run again.
- For **source installs**, use:
```bash

View File

@@ -321,7 +321,7 @@ Since the Pi is just the Gateway (models run in the cloud), use API-based models
## Auto-Start on Boot
The setup wizard sets this up, but to verify:
Onboarding sets this up, but to verify:
```bash
# Check service is enabled

View File

@@ -16,15 +16,15 @@ Ollama is a local LLM runtime that makes it easy to run open-source models on yo
## Quick start
### Onboarding wizard (recommended)
### Onboarding (recommended)
The fastest way to set up Ollama is through the setup wizard:
The fastest way to set up Ollama is through onboarding:
```bash
openclaw onboard
```
Select **Ollama** from the provider list. The wizard will:
Select **Ollama** from the provider list. Onboarding will:
1. Ask for the Ollama base URL where your instance can be reached (default `http://127.0.0.1:11434`).
2. Let you choose **Cloud + Local** (cloud models and local models) or **Local** (local models only).

View File

@@ -1,24 +1,24 @@
---
summary: "Full reference for the CLI setup wizard: every step, flag, and config field"
summary: "Full reference for CLI onboarding: every step, flag, and config field"
read_when:
- Looking up a specific wizard step or flag
- Looking up a specific onboarding step or flag
- Automating onboarding with non-interactive mode
- Debugging wizard behavior
title: "Setup Wizard Reference"
sidebarTitle: "Wizard Reference"
- Debugging onboarding behavior
title: "Onboarding Reference"
sidebarTitle: "Onboarding Reference"
---
# Setup Wizard Reference
# Onboarding Reference
This is the full reference for the `openclaw onboard` CLI wizard.
For a high-level overview, see [Setup Wizard](/start/wizard).
This is the full reference for `openclaw onboard`.
For a high-level overview, see [Onboarding (CLI)](/start/wizard).
## Flow details (local mode)
<Steps>
<Step title="Existing config detection">
- If `~/.openclaw/openclaw.json` exists, choose **Keep / Modify / Reset**.
- Re-running the wizard does **not** wipe anything unless you explicitly choose **Reset**
- Re-running onboarding does **not** wipe anything unless you explicitly choose **Reset**
(or pass `--reset`).
- CLI `--reset` defaults to `config+creds+sessions`; use `--reset-scope full`
to also remove workspace.
@@ -31,9 +31,9 @@ For a high-level overview, see [Setup Wizard](/start/wizard).
</Step>
<Step title="Model/Auth">
- **Anthropic API key**: uses `ANTHROPIC_API_KEY` if present or prompts for a key, then saves it for daemon use.
- **Anthropic OAuth (Claude Code CLI)**: on macOS the wizard checks Keychain item "Claude Code-credentials" (choose "Always Allow" so launchd starts don't block); on Linux/Windows it reuses `~/.claude/.credentials.json` if present.
- **Anthropic OAuth (Claude Code CLI)**: on macOS onboarding checks Keychain item "Claude Code-credentials" (choose "Always Allow" so launchd starts don't block); on Linux/Windows it reuses `~/.claude/.credentials.json` if present.
- **Anthropic token (paste setup-token)**: run `claude setup-token` on any machine, then paste the token (you can name it; blank = default).
- **OpenAI Code (Codex) subscription (Codex CLI)**: if `~/.codex/auth.json` exists, the wizard can reuse it.
- **OpenAI Code (Codex) subscription (Codex CLI)**: if `~/.codex/auth.json` exists, onboarding can reuse it.
- **OpenAI Code (Codex) subscription (OAuth)**: browser flow; paste the `code#state`.
- Sets `agents.defaults.model` to `openai-codex/gpt-5.2` when model is unset or `openai/*`.
- **OpenAI API key**: uses `OPENAI_API_KEY` if present or prompts for a key, then stores it in auth profiles.
@@ -55,7 +55,7 @@ For a high-level overview, see [Setup Wizard](/start/wizard).
- More detail: [Moonshot AI (Kimi + Kimi Coding)](/providers/moonshot)
- **Skip**: no auth configured yet.
- Pick a default model from detected options (or enter provider/model manually). For best quality and lower prompt-injection risk, choose the strongest latest-generation model available in your provider stack.
- Wizard runs a model check and warns if the configured model is unknown or missing auth.
- Onboarding runs a model check and warns if the configured model is unknown or missing auth.
- API key storage mode defaults to plaintext auth-profile values. Use `--secret-input-mode ref` to store env-backed refs instead (for example `keyRef: { source: "env", provider: "default", id: "OPENAI_API_KEY" }`).
- OAuth credentials live in `~/.openclaw/credentials/oauth.json`; auth profiles live in `~/.openclaw/agents/<agentId>/agent/auth-profiles.json` (API keys + OAuth).
- More detail: [/concepts/oauth](/concepts/oauth)
@@ -106,7 +106,7 @@ For a high-level overview, see [Setup Wizard](/start/wizard).
- macOS: LaunchAgent
- Requires a logged-in user session; for headless, use a custom LaunchDaemon (not shipped).
- Linux (and Windows via WSL2): systemd user unit
- Wizard attempts to enable lingering via `loginctl enable-linger <user>` so the Gateway stays up after logout.
- Onboarding attempts to enable lingering via `loginctl enable-linger <user>` so the Gateway stays up after logout.
- May prompt for sudo (writes `/var/lib/systemd/linger`); it tries without sudo first.
- **Runtime selection:** Node (recommended; required for WhatsApp/Telegram). Bun is **not recommended**.
- If token auth requires a token and `gateway.auth.token` is SecretRef-managed, daemon install validates it but does not persist resolved plaintext token values into supervisor service environment metadata.
@@ -128,8 +128,8 @@ For a high-level overview, see [Setup Wizard](/start/wizard).
</Steps>
<Note>
If no GUI is detected, the wizard prints SSH port-forward instructions for the Control UI instead of opening a browser.
If the Control UI assets are missing, the wizard attempts to build them; fallback is `pnpm ui:build` (auto-installs UI deps).
If no GUI is detected, onboarding prints SSH port-forward instructions for the Control UI instead of opening a browser.
If the Control UI assets are missing, onboarding attempts to build them; fallback is `pnpm ui:build` (auto-installs UI deps).
</Note>
## Non-interactive mode
@@ -183,12 +183,12 @@ openclaw agents add work \
## Gateway wizard RPC
The Gateway exposes the wizard flow over RPC (`wizard.start`, `wizard.next`, `wizard.cancel`, `wizard.status`).
The Gateway exposes the onboarding flow over RPC (`wizard.start`, `wizard.next`, `wizard.cancel`, `wizard.status`).
Clients (macOS app, Control UI) can render steps without reimplementing onboarding logic.
## Signal setup (signal-cli)
The wizard can install `signal-cli` from GitHub releases:
Onboarding can install `signal-cli` from GitHub releases:
- Downloads the appropriate release asset.
- Stores it under `~/.openclaw/tools/signal-cli/<version>/`.
@@ -223,12 +223,12 @@ Typical fields in `~/.openclaw/openclaw.json`:
WhatsApp credentials go under `~/.openclaw/credentials/whatsapp/<accountId>/`.
Sessions are stored under `~/.openclaw/agents/<agentId>/sessions/`.
Some channels are delivered as plugins. When you pick one during setup, the wizard
Some channels are delivered as plugins. When you pick one during setup, onboarding
will prompt to install it (npm or a local path) before it can be configured.
## Related docs
- Wizard overview: [Setup Wizard](/start/wizard)
- Onboarding overview: [Onboarding (CLI)](/start/wizard)
- macOS app onboarding: [Onboarding](/start/onboarding)
- Config reference: [Gateway configuration](/gateway/configuration)
- Providers: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord), [Google Chat](/channels/googlechat), [Signal](/channels/signal), [BlueBubbles](/channels/bluebubbles) (iMessage), [iMessage](/channels/imessage) (legacy)

View File

@@ -52,13 +52,13 @@ Check your Node version with `node --version` if you are unsure.
</Note>
</Step>
<Step title="Run the setup wizard">
<Step title="Run onboarding">
```bash
openclaw onboard --install-daemon
```
The wizard configures auth, gateway settings, and optional channels.
See [Setup Wizard](/start/wizard) for details.
Onboarding configures auth, gateway settings, and optional channels.
See [Onboarding (CLI)](/start/wizard) for details.
</Step>
<Step title="Check the Gateway">
@@ -114,8 +114,8 @@ Full environment variable reference: [Environment vars](/help/environment).
## Go deeper
<Columns>
<Card title="Setup Wizard (details)" href="/start/wizard">
Full CLI wizard reference and advanced options.
<Card title="Onboarding (CLI)" href="/start/wizard">
Full CLI onboarding reference and advanced options.
</Card>
<Card title="macOS app onboarding" href="/start/onboarding">
First run flow for the macOS app.

View File

@@ -19,7 +19,7 @@ Use these hubs to discover every page, including deep dives and reference docs t
- [Getting Started](/start/getting-started)
- [Quick start](/start/quickstart)
- [Onboarding](/start/onboarding)
- [Wizard](/start/wizard)
- [Onboarding (CLI)](/start/wizard)
- [Setup](/start/setup)
- [Dashboard (local Gateway)](http://127.0.0.1:18789/)
- [Help](/help)

View File

@@ -14,21 +14,21 @@ and how you prefer to configure providers.
## Choose your onboarding path
- **CLI wizard** for macOS, Linux, and Windows (via WSL2).
- **CLI onboarding** for macOS, Linux, and Windows (via WSL2).
- **macOS app** for a guided first run on Apple silicon or Intel Macs.
## CLI setup wizard
## CLI onboarding
Run the wizard in a terminal:
Run onboarding in a terminal:
```bash
openclaw onboard
```
Use the CLI wizard when you want full control of the Gateway, workspace,
Use CLI onboarding when you want full control of the Gateway, workspace,
channels, and skills. Docs:
- [Setup Wizard (CLI)](/start/wizard)
- [Onboarding (CLI)](/start/wizard)
- [`openclaw onboard` command](/cli/onboard)
## macOS app onboarding
@@ -41,7 +41,7 @@ Use the OpenClaw app when you want a fully guided setup on macOS. Docs:
If you need an endpoint that is not listed, including hosted providers that
expose standard OpenAI or Anthropic APIs, choose **Custom Provider** in the
CLI wizard. You will be asked to:
CLI onboarding. You will be asked to:
- Pick OpenAI-compatible, Anthropic-compatible, or **Unknown** (auto-detect).
- Enter a base URL and API key (if required by the provider).

View File

@@ -16,7 +16,7 @@ Quick start is now part of [Getting Started](/start/getting-started).
<Card title="Getting Started" href="/start/getting-started">
Install OpenClaw and run your first chat in minutes.
</Card>
<Card title="Onboarding Wizard" href="/start/wizard">
Full CLI wizard reference and advanced options.
<Card title="Onboarding (CLI)" href="/start/wizard">
Full CLI onboarding reference and advanced options.
</Card>
</Columns>

View File

@@ -10,7 +10,7 @@ title: "Setup"
<Note>
If you are setting up for the first time, start with [Getting Started](/start/getting-started).
For wizard details, see [Onboarding Wizard](/start/wizard).
For onboarding details, see [Onboarding (CLI)](/start/wizard).
</Note>
Last updated: 2026-01-01

View File

@@ -33,7 +33,7 @@ openclaw onboard --non-interactive \
Add `--json` for a machine-readable summary.
Use `--secret-input-mode ref` to store env-backed refs in auth profiles instead of plaintext values.
Interactive selection between env refs and configured provider refs (`file` or `exec`) is available in the setup wizard flow.
Interactive selection between env refs and configured provider refs (`file` or `exec`) is available in the onboarding flow.
In non-interactive `ref` mode, provider env vars must be set in the process environment.
Passing inline key flags without the matching env var now fails fast.
@@ -210,6 +210,6 @@ Notes:
## Related docs
- Onboarding hub: [Setup Wizard (CLI)](/start/wizard)
- Onboarding hub: [Onboarding (CLI)](/start/wizard)
- Full reference: [CLI Setup Reference](/start/wizard-cli-reference)
- Command reference: [`openclaw onboard`](/cli/onboard)

View File

@@ -10,7 +10,7 @@ sidebarTitle: "CLI reference"
# CLI Setup Reference
This page is the full reference for `openclaw onboard`.
For the short guide, see [Setup Wizard (CLI)](/start/wizard).
For the short guide, see [Onboarding (CLI)](/start/wizard).
## What the wizard does
@@ -294,6 +294,6 @@ Signal setup behavior:
## Related docs
- Onboarding hub: [Setup Wizard (CLI)](/start/wizard)
- Onboarding hub: [Onboarding (CLI)](/start/wizard)
- Automation and scripts: [CLI Automation](/start/wizard-cli-automation)
- Command reference: [`openclaw onboard`](/cli/onboard)

View File

@@ -1,15 +1,15 @@
---
summary: "CLI setup wizard: guided setup for gateway, workspace, channels, and skills"
summary: "CLI onboarding: guided setup for gateway, workspace, channels, and skills"
read_when:
- Running or configuring the setup wizard
- Running or configuring CLI onboarding
- Setting up a new machine
title: "Setup Wizard (CLI)"
title: "Onboarding (CLI)"
sidebarTitle: "Onboarding: CLI"
---
# Setup Wizard (CLI)
# Onboarding (CLI)
The setup wizard is the **recommended** way to set up OpenClaw on macOS,
CLI onboarding is the **recommended** way to set up OpenClaw on macOS,
Linux, or Windows (via WSL2; strongly recommended).
It configures a local Gateway or a remote Gateway connection, plus channels, skills,
and workspace defaults in one guided flow.
@@ -35,7 +35,7 @@ openclaw agents add <name>
</Note>
<Tip>
The setup wizard includes a web search step where you can pick a provider
CLI onboarding includes a web search step where you can pick a provider
(Perplexity, Brave, Gemini, Grok, or Kimi) and paste your API key so the agent
can use `web_search`. You can also configure this later with
`openclaw configure --section web`. Docs: [Web tools](/tools/web).
@@ -43,7 +43,7 @@ can use `web_search`. You can also configure this later with
## QuickStart vs Advanced
The wizard starts with **QuickStart** (defaults) vs **Advanced** (full control).
Onboarding starts with **QuickStart** (defaults) vs **Advanced** (full control).
<Tabs>
<Tab title="QuickStart (defaults)">
@@ -61,7 +61,7 @@ The wizard starts with **QuickStart** (defaults) vs **Advanced** (full control).
</Tab>
</Tabs>
## What the wizard configures
## What onboarding configures
**Local mode (default)** walks you through these steps:
@@ -84,9 +84,9 @@ The wizard starts with **QuickStart** (defaults) vs **Advanced** (full control).
7. **Skills** — Installs recommended skills and optional dependencies.
<Note>
Re-running the wizard does **not** wipe anything unless you explicitly choose **Reset** (or pass `--reset`).
Re-running onboarding does **not** wipe anything unless you explicitly choose **Reset** (or pass `--reset`).
CLI `--reset` defaults to config, credentials, and sessions; use `--reset-scope full` to include workspace.
If the config is invalid or contains legacy keys, the wizard asks you to run `openclaw doctor` first.
If the config is invalid or contains legacy keys, onboarding asks you to run `openclaw doctor` first.
</Note>
**Remote mode** only configures the local client to connect to a Gateway elsewhere.
@@ -95,7 +95,7 @@ It does **not** install or change anything on the remote host.
## Add another agent
Use `openclaw agents add <name>` to create a separate agent with its own workspace,
sessions, and auth profiles. Running without `--workspace` launches the wizard.
sessions, and auth profiles. Running without `--workspace` launches onboarding.
What it sets:
@@ -106,7 +106,7 @@ What it sets:
Notes:
- Default workspaces follow `~/.openclaw/workspace-<agentId>`.
- Add `bindings` to route inbound messages (the wizard can do this).
- Add `bindings` to route inbound messages (onboarding can do this).
- Non-interactive flags: `--model`, `--agent-dir`, `--bind`, `--non-interactive`.
## Full reference
@@ -115,7 +115,7 @@ For detailed step-by-step breakdowns and config outputs, see
[CLI Setup Reference](/start/wizard-cli-reference).
For non-interactive examples, see [CLI Automation](/start/wizard-cli-automation).
For the deeper technical reference, including RPC details, see
[Wizard Reference](/reference/wizard).
[Onboarding Reference](/reference/wizard).
## Related docs

View File

@@ -28,7 +28,7 @@ Auth is supplied during the WebSocket handshake via:
- `connect.params.auth.token`
- `connect.params.auth.password`
The dashboard settings panel keeps a token for the current browser tab session and selected gateway URL; passwords are not persisted.
The setup wizard generates a gateway token by default, so paste it here on first connect.
Onboarding generates a gateway token by default, so paste it here on first connect.
## Device pairing (first connection)

View File

@@ -324,22 +324,22 @@ openclaw [--dev] [--profile <name>] <command>
选项:
- `--workspace <dir>`:智能体工作区路径(默认 `~/.openclaw/workspace`)。
- `--wizard`:运行设置向导。
- `--non-interactive`:无提示运行导。
- `--mode <local|remote>`导模式。
- `--wizard`:运行新手引导。
- `--non-interactive`:无提示运行新手引导。
- `--mode <local|remote>`新手引导模式。
- `--remote-url <url>`:远程 Gateway 网关 URL。
- `--remote-token <token>`:远程 Gateway 网关 token。
只要存在任意导标志(`--non-interactive`, `--mode`, `--remote-url`, `--remote-token`),就会自动运行导。
只要存在任意新手引导标志(`--non-interactive`, `--mode`, `--remote-url`, `--remote-token`),就会自动运行新手引导。
### `onboard`
用于设置 gateway、工作区和 Skills 的交互式导。
用于设置 gateway、工作区和 Skills 的交互式新手引导。
选项:
- `--workspace <dir>`
- `--reset`(在运行导前重置配置 + 凭据 + 会话)
- `--reset`(在运行新手引导前重置配置 + 凭据 + 会话)
- `--reset-scope <config|config+creds+sessions|full>`(默认 `config+creds+sessions`;使用 `full` 还会删除工作区)
- `--non-interactive`
- `--mode <local|remote>`

View File

@@ -1,7 +1,7 @@
---
read_when:
- 你想通过引导式设置来配置 Gateway 网关、工作区、身份验证、渠道和 Skills
summary: "`openclaw onboard` 的 CLI 参考(交互式设置向导)"
summary: "`openclaw onboard` 的 CLI 参考(交互式新手引导)"
title: onboard
x-i18n:
generated_at: "2026-03-16T06:21:32Z"
@@ -14,11 +14,11 @@ x-i18n:
# `openclaw onboard`
交互式设置向导(本地或远程 Gateway 网关设置)。
交互式新手引导(本地或远程 Gateway 网关设置)。
## 相关指南
- CLI 新手引导中心:[设置向导CLI](/start/wizard)
- CLI 新手引导中心:[CLI 新手引导](/start/wizard)
- 新手引导概览:[新手引导概览](/start/onboarding-overview)
- CLI 新手引导参考:[CLI 设置参考](/start/wizard-cli-reference)
- CLI 自动化:[CLI 自动化](/start/wizard-cli-automation)

View File

@@ -1,6 +1,6 @@
---
read_when:
- 你正在进行首次运行设置,但不使用完整的设置向
- 你正在进行首次运行设置,但不使用完整的 CLI 新手引
- 你想设置默认工作区路径
summary: "`openclaw setup` 的 CLI 参考(初始化配置 + 工作区)"
title: setup
@@ -20,7 +20,7 @@ x-i18n:
相关内容:
- 入门指南:[入门指南](/start/getting-started)
- 向导:[新手引导](/start/onboarding)
- CLI 新手引导:[CLI 新手引导](/start/wizard)
## 示例
@@ -29,7 +29,7 @@ openclaw setup
openclaw setup --workspace ~/.openclaw/workspace
```
通过 setup 运行导:
通过 setup 运行新手引导:
```bash
openclaw setup --wizard

View File

@@ -39,7 +39,7 @@ x-i18n:
- [如何在 VPS 上安装 OpenClaw](#how-do-i-install-openclaw-on-a-vps)
- [云/VPS 安装指南在哪里?](#where-are-the-cloudvps-install-guides)
- [可以让 OpenClaw 自行更新吗?](#can-i-ask-openclaw-to-update-itself)
- [新手引导向导具体做了什么?](#what-does-the-onboarding-wizard-actually-do)
- [新手引导具体做了什么?](#新手引导具体做了什么)
- [运行 OpenClaw 需要 Claude 或 OpenAI 订阅吗?](#do-i-need-a-claude-or-openai-subscription-to-run-this)
- [能否使用 Claude Max 订阅而不需要 API 密钥?](#can-i-use-claude-max-subscription-without-an-api-key)
- [Anthropic "setup-token" 认证如何工作?](#how-does-anthropic-setuptoken-auth-work)
@@ -310,14 +310,14 @@ openclaw doctor
### 安装和设置 OpenClaw 的推荐方式是什么
仓库推荐从源码运行并使用新手引导向导
仓库推荐从源码运行并使用新手引导:
```bash
curl -fsSL https://openclaw.ai/install.sh | bash
openclaw onboard --install-daemon
```
导还可以自动构建 UI 资源。新手引导后,通常在端口 **18789** 上运行 Gateway 网关。
新手引导还可以自动构建 UI 资源。新手引导后,通常在端口 **18789** 上运行 Gateway 网关。
从源码安装(贡献者/开发者):
@@ -334,7 +334,7 @@ openclaw onboard
### 新手引导后如何打开仪表板
导现在会在新手引导完成后立即使用带令牌的仪表板 URL 打开浏览器,并在摘要中打印完整链接(带令牌)。保持该标签页打开;如果没有自动启动,请在同一台机器上复制/粘贴打印的 URL。令牌保持在本地主机上——不会从浏览器获取任何内容。
新手引导现在会在完成后立即使用带令牌的仪表板 URL 打开浏览器,并在摘要中打印完整链接(带令牌)。保持该标签页打开;如果没有自动启动,请在同一台机器上复制/粘贴打印的 URL。令牌保持在本地主机上不会从浏览器获取任何内容。
### 如何在本地和远程环境中验证仪表板令牌
@@ -562,7 +562,7 @@ curl -fsSL https://openclaw.ai/install.sh | bash -s -- --install-method git
### 如何在 Linux 上安装 OpenClaw
简短回答:按照 Linux 指南操作,然后运行新手引导向导
简短回答:按照 Linux 指南操作,然后运行新手引导。
- Linux 快速路径 + 服务安装:[Linux](/platforms/linux)。
- 完整指南:[入门](/start/getting-started)。
@@ -614,7 +614,7 @@ openclaw gateway restart
文档:[更新](/cli/update)、[更新指南](/install/updating)。
### 新手引导向导具体做了什么
### 新手引导具体做了什么
`openclaw onboard` 是推荐的设置路径。在**本地模式**下,它引导你完成:
@@ -642,7 +642,7 @@ Claude Pro/Max 订阅**不包含 API 密钥**,因此这是订阅账户的正
### Anthropic setup-token 认证如何工作
`claude setup-token` 通过 Claude Code CLI 生成一个**令牌字符串**(在 Web 控制台中不可用)。你可以在**任何机器**上运行它。在导中选择 **Anthropic token (paste setup-token)** 或使用 `openclaw models auth paste-token --provider anthropic` 粘贴。令牌作为 **anthropic** 提供商的认证配置文件存储,像 API 密钥一样使用(无自动刷新)。更多详情:[OAuth](/concepts/oauth)。
`claude setup-token` 通过 Claude Code CLI 生成一个**令牌字符串**(在 Web 控制台中不可用)。你可以在**任何机器**上运行它。在新手引导中选择 **Anthropic token (paste setup-token)** 或使用 `openclaw models auth paste-token --provider anthropic` 粘贴。令牌作为 **anthropic** 提供商的认证配置文件存储,像 API 密钥一样使用(无自动刷新)。更多详情:[OAuth](/concepts/oauth)。
### 在哪里获取 Anthropic setup-token
@@ -652,7 +652,7 @@ Claude Pro/Max 订阅**不包含 API 密钥**,因此这是订阅账户的正
claude setup-token
```
复制它打印的令牌,然后在导中选择 **Anthropic token (paste setup-token)**。如果你想在 Gateway 网关主机上运行,使用 `openclaw models auth setup-token --provider anthropic`。如果你在其他地方运行了 `claude setup-token`,在 Gateway 网关主机上使用 `openclaw models auth paste-token --provider anthropic` 粘贴。参阅 [Anthropic](/providers/anthropic)。
复制它打印的令牌,然后在新手引导中选择 **Anthropic token (paste setup-token)**。如果你想在 Gateway 网关主机上运行,使用 `openclaw models auth setup-token --provider anthropic`。如果你在其他地方运行了 `claude setup-token`,在 Gateway 网关主机上使用 `openclaw models auth paste-token --provider anthropic` 粘贴。参阅 [Anthropic](/providers/anthropic)。
### 是否支持 Claude 订阅认证Claude Pro/Max
@@ -673,13 +673,13 @@ claude setup-token
### Codex 认证如何工作
OpenClaw 通过 OAuthChatGPT 登录)支持 **OpenAI Code (Codex)**。导可以运行 OAuth 流程,并在适当时将默认模型设置为 `openai-codex/gpt-5.2`。参阅[模型提供商](/concepts/model-providers)和[导](/start/wizard)。
OpenClaw 通过 OAuthChatGPT 登录)支持 **OpenAI Code (Codex)**。新手引导可以运行 OAuth 流程,并在适当时将默认模型设置为 `openai-codex/gpt-5.2`。参阅[模型提供商](/concepts/model-providers)和[CLI 新手引导](/start/wizard)。
### 是否支持 OpenAI 订阅认证Codex OAuth
是的。OpenClaw 完全支持 **OpenAI Code (Codex) 订阅 OAuth**。新手引导向导可以为你运行 OAuth 流程。
是的。OpenClaw 完全支持 **OpenAI Code (Codex) 订阅 OAuth**。新手引导可以为你运行 OAuth 流程。
参阅 [OAuth](/concepts/oauth)、[模型提供商](/concepts/model-providers)和[导](/start/wizard)。
参阅 [OAuth](/concepts/oauth)、[模型提供商](/concepts/model-providers)和[CLI 新手引导](/start/wizard)。
### 如何设置 Gemini CLI OAuth
@@ -1632,7 +1632,7 @@ openclaw onboard --install-daemon
注意:
- 新手引导向导在看到现有配置时也提供**重置**选项。参阅[导](/start/wizard)。
- 新手引导在看到现有配置时也提供**重置**选项。参阅[CLI 新手引导](/start/wizard)。
- 如果你使用了配置文件(`--profile` / `OPENCLAW_PROFILE`),重置每个状态目录(默认为 `~/.openclaw-<profile>`)。
- 开发重置:`openclaw gateway --dev --reset`(仅限开发;清除开发配置 + 凭据 + 会话 + 工作区)。

View File

@@ -40,7 +40,7 @@ x-i18n:
<Card title="入门指南" href="/start/getting-started" icon="rocket">
安装 OpenClaw 并在几分钟内启动 Gateway 网关。
</Card>
<Card title="运行导" href="/start/wizard" icon="sparkles">
<Card title="运行新手引导" href="/start/wizard" icon="sparkles">
通过 `openclaw onboard` 和配对流程进行引导式设置。
</Card>
<Card title="打开控制界面" href="/web/control-ui" icon="layout-dashboard">

View File

@@ -60,13 +60,13 @@ x-i18n:
</Note>
</Step>
<Step title="运行设置向导">
<Step title="运行新手引导">
```bash
openclaw onboard --install-daemon
```
导会配置认证、Gateway 网关设置和可选渠道。
详情请参见 [Setup Wizard](/start/wizard)。
新手引导会配置认证、Gateway 网关设置和可选渠道。
详情请参见 [CLI 新手引导](/start/wizard)。
</Step>
<Step title="检查 Gateway 网关">
@@ -122,8 +122,8 @@ x-i18n:
## 深入了解
<Columns>
<Card title="设置向导(详情)" href="/start/wizard">
完整的 CLI 导参考和高级选项。
<Card title="CLI 新手引导" href="/start/wizard">
完整的 CLI 新手引导参考和高级选项。
</Card>
<Card title="macOS 应用新手引导" href="/start/onboarding">
macOS 应用的首次运行流程。

View File

@@ -26,7 +26,7 @@ x-i18n:
- [入门指南](/start/getting-started)
- [快速开始](/start/quickstart)
- [新手引导](/start/onboarding)
- [](/start/wizard)
- [CLI 新手引](/start/wizard)
- [安装配置](/start/setup)
- [仪表盘(本地 Gateway 网关)](http://127.0.0.1:18789/)
- [帮助](/help)

View File

@@ -21,21 +21,21 @@ OpenClaw 支持多种新手引导路径,具体取决于 Gateway 网关运行
## 选择你的新手引导路径
- 适用于 macOS、Linux 和 Windows通过 WSL2**CLI 导**
- 适用于 macOS、Linux 和 Windows通过 WSL2**CLI 新手引导**
- 适用于 Apple silicon 或 Intel Mac 的 **macOS 应用**,提供引导式首次运行体验。
## CLI 设置向
## CLI 新手引
在终端中运行导:
在终端中运行新手引导:
```bash
openclaw onboard
```
当你希望完全控制 Gateway 网关、工作区、
渠道和 Skills 时,请使用 CLI 导。文档:
渠道和 Skills 时,请使用 CLI 新手引导。文档:
- [设置向导CLI](/start/wizard)
- [CLI 新手引导](/start/wizard)
- [`openclaw onboard` 命令](/cli/onboard)
## macOS 应用新手引导
@@ -48,7 +48,7 @@ openclaw onboard
如果你需要一个未列出的端点,包括那些
公开标准 OpenAI 或 Anthropic API 的托管提供商,请在
CLI 导中选择 **Custom Provider**。系统会要求你:
CLI 新手引导中选择 **Custom Provider**。系统会要求你:
- 选择兼容 OpenAI、兼容 Anthropic**Unknown**(自动检测)。
- 输入基础 URL 和 API 密钥(如果提供商需要)。

View File

@@ -1,10 +1,10 @@
---
read_when:
- 运行或配置设置向
- 运行或配置 CLI 新手引
- 设置一台新机器
sidebarTitle: "Onboarding: CLI"
summary: CLI 设置向导:用于 Gateway 网关、工作区、渠道和 Skills 的引导式设置
title: 设置向导CLI
summary: CLI 新手引导:用于 Gateway 网关、工作区、渠道和 Skills 的引导式设置
title: CLI 新手引导
x-i18n:
generated_at: "2026-03-16T06:28:38Z"
model: gpt-5.4
@@ -14,9 +14,9 @@ x-i18n:
workflow: 15
---
# 设置向导CLI
# CLI 新手引导
设置向导是在 macOS、
CLI 新手引导是在 macOS、
Linux 或 Windows通过 WSL2强烈推荐上设置 OpenClaw 的**推荐**方式。
它可在一次引导式流程中配置本地 Gateway 网关或远程 Gateway 网关连接以及渠道、Skills
和工作区默认值。
@@ -42,7 +42,7 @@ openclaw agents add <name>
</Note>
<Tip>
设置向导包含一个 web search 步骤,你可以选择一个提供商
CLI 新手引导包含一个 web search 步骤,你可以选择一个提供商
Perplexity、Brave、Gemini、Grok 或 Kimi并粘贴你的 API 密钥,以便智能体
可以使用 `web_search`。你也可以稍后通过
`openclaw configure --section web` 进行配置。文档:[Web 工具](/tools/web)。
@@ -50,7 +50,7 @@ openclaw agents add <name>
## 快速开始与高级模式
导开始时会让你选择**快速开始**(默认值)或**高级模式**(完全控制)。
新手引导开始时会让你选择**快速开始**(默认值)或**高级模式**(完全控制)。
<Tabs>
<Tab title="快速开始(默认值)">
@@ -68,7 +68,7 @@ openclaw agents add <name>
</Tab>
</Tabs>
## 导会配置什么
## 新手引导会配置什么
**本地模式(默认)**会引导你完成以下步骤:
@@ -91,9 +91,9 @@ openclaw agents add <name>
7. **Skills** —— 安装推荐的 Skills 和可选依赖项。
<Note>
重新运行导**不会**清除任何内容,除非你显式选择 **Reset**(或传入 `--reset`)。
重新运行新手引导**不会**清除任何内容,除非你显式选择 **Reset**(或传入 `--reset`)。
CLI `--reset` 默认会重置配置、凭证和会话;如需包含工作区,请使用 `--reset-scope full`
如果配置无效或包含旧版键,导会先要求你运行 `openclaw doctor`
如果配置无效或包含旧版键,新手引导会先要求你运行 `openclaw doctor`
</Note>
**远程模式**只会配置本地客户端以连接到其他地方的 Gateway 网关。
@@ -102,7 +102,7 @@ CLI `--reset` 默认会重置配置、凭证和会话;如需包含工作区,
## 添加另一个智能体
使用 `openclaw agents add <name>` 创建一个单独的智能体,它拥有自己的工作区、
会话和认证配置文件。不带 `--workspace` 运行会启动导。
会话和认证配置文件。不带 `--workspace` 运行会启动新手引导。
它会设置:
@@ -113,7 +113,7 @@ CLI `--reset` 默认会重置配置、凭证和会话;如需包含工作区,
说明:
- 默认工作区遵循 `~/.openclaw/workspace-<agentId>`
- 添加 `bindings` 以路由入站消息(导可以完成这项操作)。
- 添加 `bindings` 以路由入站消息(新手引导可以完成这项操作)。
- 非交互式标志:`--model``--agent-dir``--bind``--non-interactive`
## 完整参考
@@ -122,7 +122,7 @@ CLI `--reset` 默认会重置配置、凭证和会话;如需包含工作区,
[CLI 设置参考](/start/wizard-cli-reference)。
有关非交互式示例,请参见 [CLI 自动化](/start/wizard-cli-automation)。
有关更深入的技术参考(包括 RPC 细节),请参见
[导参考](/reference/wizard)。
[新手引导参考](/reference/wizard)。
## 相关文档

View File

@@ -127,17 +127,14 @@ export function applyGroupGating(params: ApplyGroupGatingParams) {
conversationId: params.conversationId,
});
const requireMention = activation !== "always";
const selfJid = params.msg.selfJid?.replace(/:\d+/, "");
const selfLid = params.msg.selfLid?.replace(/:\d+/, "");
// replyToSenderJid may carry either a standard JID or an @lid identifier.
const replySenderJid = params.msg.replyToSenderJid?.replace(/:\d+/, "");
const selfJid = params.msg.selfJid?.replace(/:\\d+/, "");
const replySenderJid = params.msg.replyToSenderJid?.replace(/:\\d+/, "");
const selfE164 = params.msg.selfE164 ? normalizeE164(params.msg.selfE164) : null;
const replySenderE164 = params.msg.replyToSenderE164
? normalizeE164(params.msg.replyToSenderE164)
: null;
const implicitMention = Boolean(
(selfJid && replySenderJid && selfJid === replySenderJid) ||
(selfLid && replySenderJid && selfLid === replySenderJid) ||
(selfE164 && replySenderE164 && selfE164 === replySenderE164),
);
const mentionGate = resolveMentionGating({

View File

@@ -112,7 +112,7 @@ describe("applyGroupGating", () => {
accountId: "default",
body: "following up",
timestamp: Date.now(),
selfJid: "15551234567:1@s.whatsapp.net",
selfJid: "15551234567@s.whatsapp.net",
selfE164: "+15551234567",
replyToId: "m0",
replyToBody: "bot said hi",
@@ -125,29 +125,6 @@ describe("applyGroupGating", () => {
expect(result.shouldProcess).toBe(true);
});
it("treats LID-format reply-to-bot as implicit mention", () => {
const cfg = makeConfig({});
const { result } = runGroupGating({
cfg,
msg: createGroupMessage({
id: "m1-lid",
to: "+15550000",
accountId: "default",
body: "following up",
timestamp: Date.now(),
selfJid: "15551234567@s.whatsapp.net",
selfLid: "1234567890123:1@lid",
selfE164: "+15551234567",
replyToId: "m0",
replyToBody: "bot said hi",
replyToSender: "1234567890123@lid",
replyToSenderJid: "1234567890123@lid",
}),
});
expect(result.shouldProcess).toBe(true);
});
it.each([
{ id: "g-new", command: "/new" },
{ id: "g-status", command: "/status" },

View File

@@ -66,7 +66,6 @@ export async function monitorWebInbox(options: {
}
const selfJid = sock.user?.id;
const selfLid = sock.user?.lid;
const selfE164 = selfJid ? jidToE164(selfJid) : null;
const debouncer = createInboundDebouncer<WebInboundMessage>({
debounceMs: options.debounceMs ?? 0,
@@ -373,7 +372,6 @@ export async function monitorWebInbox(options: {
groupParticipants: inbound.groupParticipants,
mentionedJids: mentionedJids ?? undefined,
selfJid,
selfLid,
selfE164,
fromMe: Boolean(msg.key?.fromMe),
location: enriched.location ?? undefined,

View File

@@ -30,7 +30,6 @@ export type WebInboundMessage = {
groupParticipants?: string[];
mentionedJids?: string[];
selfJid?: string | null;
selfLid?: string | null;
selfE164?: string | null;
fromMe?: boolean;
location?: NormalizedLocation;

View File

@@ -118,12 +118,7 @@ describe("web monitor inbox", () => {
await tick();
expect(onMessage).toHaveBeenCalledWith(
expect.objectContaining({
body: "ping",
from: "+999",
to: "+123",
selfLid: "123:1@lid",
}),
expect.objectContaining({ body: "ping", from: "+999", to: "+123" }),
);
expect(sock.readMessages).toHaveBeenCalledWith([
{
@@ -186,12 +181,7 @@ describe("web monitor inbox", () => {
expect(getPNForLID).toHaveBeenCalledWith("999@lid");
expect(onMessage).toHaveBeenCalledWith(
expect.objectContaining({
body: "ping",
from: "+999",
to: "+123",
selfLid: "123:1@lid",
}),
expect.objectContaining({ body: "ping", from: "+999", to: "+123" }),
);
await listener.close();

View File

@@ -44,7 +44,7 @@ export type MockSock = {
getPNForLID: AnyMockFn;
};
};
user: { id: string; lid?: string };
user: { id: string };
};
function createResolvedMock() {
@@ -66,7 +66,7 @@ function createMockSock(): MockSock {
getPNForLID: vi.fn().mockResolvedValue(null),
},
},
user: { id: "123@s.whatsapp.net", lid: "123:1@lid" },
user: { id: "123@s.whatsapp.net" },
};
}

View File

@@ -64,10 +64,7 @@ import { ensureRuntimePluginsLoaded } from "../runtime-plugins.js";
import { resolveSandboxContext } from "../sandbox.js";
import { repairSessionFileIfNeeded } from "../session-file-repair.js";
import { guardSessionManager } from "../session-tool-result-guard-wrapper.js";
import {
repairToolUseResultPairing,
sanitizeToolUseResultPairing,
} from "../session-transcript-repair.js";
import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js";
import {
acquireSessionWriteLock,
resolveSessionLockMaxHoldFromTimeout,
@@ -957,22 +954,6 @@ export async function compactEmbeddedPiSessionDirect(
},
},
);
// Re-run tool_use/tool_result pairing repair after compaction.
// Compaction can remove assistant messages containing tool_use blocks
// while leaving orphaned tool_result blocks behind, which causes
// Anthropic API 400 errors: "unexpected tool_use_id found in tool_result blocks".
// See: https://github.com/openclaw/openclaw/issues/15691
if (transcriptPolicy.repairToolUseResultPairing) {
const postCompactRepair = repairToolUseResultPairing(session.messages);
if (postCompactRepair.droppedOrphanCount > 0 || postCompactRepair.moved) {
session.agent.replaceMessages(postCompactRepair.messages);
log.info(
`[compaction] post-compact repair: dropped ${postCompactRepair.droppedOrphanCount} orphaned tool_result(s), ` +
`${postCompactRepair.droppedDuplicateCount} duplicate(s) ` +
`(sessionKey=${params.sessionKey ?? params.sessionId})`,
);
}
}
await runPostCompactionSideEffects({
config: params.config,
sessionKey: params.sessionKey,

View File

@@ -28,8 +28,6 @@ const mockSummarizeInStages = vi.mocked(compactionModule.summarizeInStages);
const {
collectToolFailures,
formatToolFailuresSection,
trimToolResultsForSummarization,
restoreOriginalToolResultsForKeptMessages,
splitPreservedRecentTurns,
formatPreservedTurnsSection,
buildCompactionStructureInstructions,
@@ -47,26 +45,6 @@ const {
SAFETY_MARGIN,
} = __testing;
function readTextBlocks(message: AgentMessage): string {
const content = (message as { content?: unknown }).content;
if (typeof content === "string") {
return content;
}
if (!Array.isArray(content)) {
return "";
}
return content
.map((block) => {
if (!block || typeof block !== "object") {
return "";
}
const text = (block as { text?: unknown }).text;
return typeof text === "string" ? text : "";
})
.filter(Boolean)
.join("\n");
}
function stubSessionManager(): ExtensionContext["sessionManager"] {
const stub: ExtensionContext["sessionManager"] = {
getCwd: () => "/stub",
@@ -256,116 +234,6 @@ describe("compaction-safeguard tool failures", () => {
});
});
describe("compaction-safeguard toolResult trimming", () => {
it("truncates oversized tool results and compacts older entries to stay within budget", () => {
const messages: AgentMessage[] = Array.from({ length: 9 }, (_unused, index) => ({
role: "toolResult",
toolCallId: `call-${index}`,
toolName: "read",
content: [
{
type: "text",
text: `head-${index}\n${"x".repeat(25_000)}\ntail-${index}`,
},
],
timestamp: index + 1,
})) as AgentMessage[];
const trimmed = trimToolResultsForSummarization(messages);
expect(trimmed.stats.truncatedCount).toBe(9);
expect(trimmed.stats.compactedCount).toBe(1);
expect(readTextBlocks(trimmed.messages[0])).toBe("");
expect(trimmed.stats.afterChars).toBeLessThan(trimmed.stats.beforeChars);
expect(readTextBlocks(trimmed.messages[8])).toContain("head-8");
expect(readTextBlocks(trimmed.messages[8])).toContain(
"[...tool result truncated for compaction budget...]",
);
expect(readTextBlocks(trimmed.messages[8])).toContain("tail-8");
});
it("restores kept tool results after prune for both toolCallId and toolUseId", () => {
const originalMessages: AgentMessage[] = [
{ role: "user", content: "keep these tool results", timestamp: 1 },
{
role: "toolResult",
toolCallId: "call-1",
toolName: "read",
content: [{ type: "text", text: "original call payload" }],
timestamp: 2,
} as AgentMessage,
{
role: "toolResult",
toolUseId: "use-1",
toolName: "exec",
content: [{ type: "text", text: "original use payload" }],
timestamp: 3,
} as unknown as AgentMessage,
];
const prunedMessages: AgentMessage[] = [
originalMessages[0],
{
role: "toolResult",
toolCallId: "call-1",
toolName: "read",
content: [{ type: "text", text: "trimmed call payload" }],
timestamp: 2,
} as AgentMessage,
{
role: "toolResult",
toolUseId: "use-1",
toolName: "exec",
content: [{ type: "text", text: "trimmed use payload" }],
timestamp: 3,
} as unknown as AgentMessage,
];
const restored = restoreOriginalToolResultsForKeptMessages({
prunedMessages,
originalMessages,
});
expect(readTextBlocks(restored[1])).toBe("original call payload");
expect(readTextBlocks(restored[2])).toBe("original use payload");
});
it("extracts identifiers from the trimmed kept payloads after prune restore", () => {
const hiddenIdentifier = "DEADBEEF12345678";
const restored = restoreOriginalToolResultsForKeptMessages({
prunedMessages: [
{ role: "user", content: "recent ask", timestamp: 1 },
{
role: "toolResult",
toolCallId: "call-1",
toolName: "read",
content: [{ type: "text", text: "placeholder" }],
timestamp: 2,
} as AgentMessage,
],
originalMessages: [
{ role: "user", content: "recent ask", timestamp: 1 },
{
role: "toolResult",
toolCallId: "call-1",
toolName: "read",
content: [
{
type: "text",
text: `visible head ${"a".repeat(16_000)}${hiddenIdentifier}${"b".repeat(16_000)} visible tail`,
},
],
timestamp: 2,
} as AgentMessage,
],
});
const trimmed = trimToolResultsForSummarization(restored).messages;
const identifierSeedText = trimmed.map((message) => readTextBlocks(message)).join("\n");
expect(extractOpaqueIdentifiers(identifierSeedText)).not.toContain(hiddenIdentifier);
});
});
describe("computeAdaptiveChunkRatio", () => {
const CONTEXT_WINDOW = 200_000;

View File

@@ -407,179 +407,6 @@ function formatPreservedTurnsSection(messages: AgentMessage[]): string {
return `\n\n## Recent turns preserved verbatim\n${lines.join("\n")}`;
}
type ToolResultSummaryTrimStats = {
truncatedCount: number;
compactedCount: number;
beforeChars: number;
afterChars: number;
};
const COMPACTION_SUMMARY_TOOL_RESULT_MAX_CHARS = 2_500;
const COMPACTION_SUMMARY_TOOL_RESULT_TOTAL_CHARS_BUDGET = 20_000;
const COMPACTION_SUMMARY_TOOL_RESULT_TRUNCATION_NOTICE =
"[...tool result truncated for compaction budget...]";
const COMPACTION_SUMMARY_TOOL_RESULT_COMPACTED_NOTICE =
"[tool result compacted due to global compaction budget]";
const COMPACTION_SUMMARY_TOOL_RESULT_NON_TEXT_NOTICE = "[non-text tool result content omitted]";
function getToolResultTextFromContent(content: unknown): string {
if (!Array.isArray(content)) {
return "";
}
const parts: string[] = [];
for (const block of content) {
if (!block || typeof block !== "object") {
continue;
}
const text = (block as { text?: unknown }).text;
if (typeof text === "string" && text.length > 0) {
parts.push(text);
}
}
return parts.join("\n");
}
function hasNonTextToolResultContent(content: unknown): boolean {
if (!Array.isArray(content)) {
return false;
}
return content.some((block) => {
if (!block || typeof block !== "object") {
return false;
}
const t = (block as { type?: unknown }).type;
return t !== "text";
});
}
function replaceToolResultContentForSummary(msg: AgentMessage, text: string): AgentMessage {
return {
...(msg as unknown as Record<string, unknown>),
content: [{ type: "text", text }],
} as AgentMessage;
}
function trimToolResultsForSummarization(messages: AgentMessage[]): {
messages: AgentMessage[];
stats: ToolResultSummaryTrimStats;
} {
const next = [...messages];
let truncatedCount = 0;
let compactedCount = 0;
let beforeChars = 0;
for (let i = 0; i < next.length; i += 1) {
const msg = next[i];
if ((msg as { role?: unknown }).role !== "toolResult") {
continue;
}
const content = (msg as { content?: unknown }).content;
const text = getToolResultTextFromContent(content);
const hasNonText = hasNonTextToolResultContent(content);
beforeChars += text.length;
let normalized = text;
if (normalized.length === 0 && hasNonText) {
normalized = COMPACTION_SUMMARY_TOOL_RESULT_NON_TEXT_NOTICE;
}
if (normalized.length > COMPACTION_SUMMARY_TOOL_RESULT_MAX_CHARS) {
const separator = `\n\n${COMPACTION_SUMMARY_TOOL_RESULT_TRUNCATION_NOTICE}\n\n`;
const available = Math.max(0, COMPACTION_SUMMARY_TOOL_RESULT_MAX_CHARS - separator.length);
const tailBudget = Math.floor(available * 0.35);
const headBudget = Math.max(0, available - tailBudget);
const head = normalized.slice(0, headBudget);
const tail = tailBudget > 0 ? normalized.slice(-tailBudget) : "";
normalized = `${head}${separator}${tail}`;
truncatedCount += 1;
}
if (hasNonText || normalized !== text) {
next[i] = replaceToolResultContentForSummary(msg, normalized);
}
}
let runningChars = 0;
for (let i = next.length - 1; i >= 0; i -= 1) {
const msg = next[i];
if ((msg as { role?: unknown }).role !== "toolResult") {
continue;
}
const text = getToolResultTextFromContent((msg as { content?: unknown }).content);
if (runningChars + text.length <= COMPACTION_SUMMARY_TOOL_RESULT_TOTAL_CHARS_BUDGET) {
runningChars += text.length;
continue;
}
const placeholderLen = COMPACTION_SUMMARY_TOOL_RESULT_COMPACTED_NOTICE.length;
const remainingBudget = Math.max(
0,
COMPACTION_SUMMARY_TOOL_RESULT_TOTAL_CHARS_BUDGET - runningChars,
);
const replacementText =
remainingBudget >= placeholderLen ? COMPACTION_SUMMARY_TOOL_RESULT_COMPACTED_NOTICE : "";
next[i] = replaceToolResultContentForSummary(msg, replacementText);
runningChars += replacementText.length;
compactedCount += 1;
}
let afterChars = 0;
for (const msg of next) {
if ((msg as { role?: unknown }).role !== "toolResult") {
continue;
}
afterChars += getToolResultTextFromContent((msg as { content?: unknown }).content).length;
}
return {
messages: next,
stats: { truncatedCount, compactedCount, beforeChars, afterChars },
};
}
function getToolResultStableId(message: AgentMessage): string | null {
if ((message as { role?: unknown }).role !== "toolResult") {
return null;
}
const toolCallId = (message as { toolCallId?: unknown }).toolCallId;
if (typeof toolCallId === "string" && toolCallId.length > 0) {
return `call:${toolCallId}`;
}
const toolUseId = (message as { toolUseId?: unknown }).toolUseId;
if (typeof toolUseId === "string" && toolUseId.length > 0) {
return `use:${toolUseId}`;
}
return null;
}
function restoreOriginalToolResultsForKeptMessages(params: {
prunedMessages: AgentMessage[];
originalMessages: AgentMessage[];
}): AgentMessage[] {
const originalByStableId = new Map<string, AgentMessage[]>();
for (const message of params.originalMessages) {
const stableId = getToolResultStableId(message);
if (!stableId) {
continue;
}
const bucket = originalByStableId.get(stableId) ?? [];
bucket.push(message);
originalByStableId.set(stableId, bucket);
}
return params.prunedMessages.map((message) => {
const stableId = getToolResultStableId(message);
if (!stableId) {
return message;
}
const bucket = originalByStableId.get(stableId);
if (!bucket || bucket.length === 0) {
return message;
}
const restored = bucket.shift();
return restored ?? message;
});
}
function wrapUntrustedInstructionBlock(label: string, text: string): string {
return wrapUntrustedPromptDataBlock({
label,
@@ -928,18 +755,6 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
const modelContextWindow = resolveContextWindowTokens(model);
const contextWindowTokens = runtime?.contextWindowTokens ?? modelContextWindow;
const turnPrefixMessages = preparation.turnPrefixMessages ?? [];
const prefixTrimmedForBudget = trimToolResultsForSummarization(turnPrefixMessages);
if (
prefixTrimmedForBudget.stats.truncatedCount > 0 ||
prefixTrimmedForBudget.stats.compactedCount > 0
) {
log.warn(
`Compaction safeguard: pre-trimmed prefix toolResult payloads for budgeting ` +
`(truncated=${prefixTrimmedForBudget.stats.truncatedCount}, compacted=${prefixTrimmedForBudget.stats.compactedCount}, ` +
`chars=${prefixTrimmedForBudget.stats.beforeChars}->${prefixTrimmedForBudget.stats.afterChars})`,
);
}
const prefixMessagesForSummary = prefixTrimmedForBudget.messages;
let messagesToSummarize = preparation.messagesToSummarize;
const recentTurnsPreserve = resolveRecentTurnsPreserve(runtime?.recentTurnsPreserve);
const qualityGuardEnabled = runtime?.qualityGuardEnabled ?? false;
@@ -959,44 +774,28 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
let droppedSummary: string | undefined;
if (tokensBefore !== undefined) {
const budgetTrimmedForSummary = trimToolResultsForSummarization(messagesToSummarize);
if (
budgetTrimmedForSummary.stats.truncatedCount > 0 ||
budgetTrimmedForSummary.stats.compactedCount > 0
) {
log.warn(
`Compaction safeguard: pre-trimmed toolResult payloads for budgeting ` +
`(truncated=${budgetTrimmedForSummary.stats.truncatedCount}, compacted=${budgetTrimmedForSummary.stats.compactedCount}, ` +
`chars=${budgetTrimmedForSummary.stats.beforeChars}->${budgetTrimmedForSummary.stats.afterChars})`,
);
}
const summarizableTokens =
estimateMessagesTokens(budgetTrimmedForSummary.messages) +
estimateMessagesTokens(prefixMessagesForSummary);
estimateMessagesTokens(messagesToSummarize) + estimateMessagesTokens(turnPrefixMessages);
const newContentTokens = Math.max(0, Math.floor(tokensBefore - summarizableTokens));
// Apply SAFETY_MARGIN so token underestimates don't trigger unnecessary pruning
const maxHistoryTokens = Math.floor(contextWindowTokens * maxHistoryShare * SAFETY_MARGIN);
if (newContentTokens > maxHistoryTokens) {
const originalMessagesBeforePrune = messagesToSummarize;
const pruned = pruneHistoryForContextShare({
messages: budgetTrimmedForSummary.messages,
messages: messagesToSummarize,
maxContextTokens: contextWindowTokens,
maxHistoryShare,
parts: 2,
});
if (pruned.droppedChunks > 0) {
const historyRatio = (summarizableTokens / contextWindowTokens) * 100;
const newContentRatio = (newContentTokens / contextWindowTokens) * 100;
log.warn(
`Compaction safeguard: summarizable history uses ${historyRatio.toFixed(
`Compaction safeguard: new content uses ${newContentRatio.toFixed(
1,
)}% of context; dropped ${pruned.droppedChunks} older chunk(s) ` +
`(${pruned.droppedMessages} messages) to fit history budget.`,
);
messagesToSummarize = restoreOriginalToolResultsForKeptMessages({
prunedMessages: pruned.messages,
originalMessages: originalMessagesBeforePrune,
});
messagesToSummarize = pruned.messages;
// Summarize dropped messages so context isn't lost
if (pruned.droppedMessagesList.length > 0) {
@@ -1010,19 +809,8 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
Math.floor(contextWindowTokens * droppedChunkRatio) -
SUMMARIZATION_OVERHEAD_TOKENS,
);
const droppedTrimmed = trimToolResultsForSummarization(pruned.droppedMessagesList);
if (
droppedTrimmed.stats.truncatedCount > 0 ||
droppedTrimmed.stats.compactedCount > 0
) {
log.warn(
`Compaction safeguard: trimmed dropped toolResult payloads before summarize ` +
`(truncated=${droppedTrimmed.stats.truncatedCount}, compacted=${droppedTrimmed.stats.compactedCount}, ` +
`chars=${droppedTrimmed.stats.beforeChars}->${droppedTrimmed.stats.afterChars})`,
);
}
droppedSummary = await summarizeInStages({
messages: droppedTrimmed.messages,
messages: pruned.droppedMessagesList,
model,
apiKey,
signal,
@@ -1054,21 +842,8 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
});
messagesToSummarize = summaryTargetMessages;
const preservedTurnsSection = formatPreservedTurnsSection(preservedRecentMessages);
const latestUserAsk = extractLatestUserAsk([
...messagesToSummarize,
...prefixMessagesForSummary,
]);
const summaryTrimmed = trimToolResultsForSummarization(messagesToSummarize);
if (summaryTrimmed.stats.truncatedCount > 0 || summaryTrimmed.stats.compactedCount > 0) {
log.warn(
`Compaction safeguard: trimmed toolResult payloads before summarize ` +
`(truncated=${summaryTrimmed.stats.truncatedCount}, compacted=${summaryTrimmed.stats.compactedCount}, ` +
`chars=${summaryTrimmed.stats.beforeChars}->${summaryTrimmed.stats.afterChars})`,
);
}
const identifierSourceMessages = [...summaryTrimmed.messages, ...prefixMessagesForSummary];
const identifierSeedText = identifierSourceMessages
const latestUserAsk = extractLatestUserAsk([...messagesToSummarize, ...turnPrefixMessages]);
const identifierSeedText = [...messagesToSummarize, ...turnPrefixMessages]
.slice(-10)
.map((message) => extractMessageText(message))
.filter(Boolean)
@@ -1078,7 +853,7 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
// Use adaptive chunk ratio based on message sizes, reserving headroom for
// the summarization prompt, system prompt, previous summary, and reasoning budget
// that generateSummary adds on top of the serialized conversation chunk.
const allMessages = [...summaryTrimmed.messages, ...prefixMessagesForSummary];
const allMessages = [...messagesToSummarize, ...turnPrefixMessages];
const adaptiveRatio = computeAdaptiveChunkRatio(allMessages, contextWindowTokens);
const maxChunkTokens = Math.max(
1,
@@ -1100,9 +875,9 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
let summaryWithPreservedTurns = "";
try {
const historySummary =
summaryTrimmed.messages.length > 0
messagesToSummarize.length > 0
? await summarizeInStages({
messages: summaryTrimmed.messages,
messages: messagesToSummarize,
model,
apiKey,
signal,
@@ -1116,9 +891,9 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
: buildStructuredFallbackSummary(effectivePreviousSummary, summarizationInstructions);
summaryWithoutPreservedTurns = historySummary;
if (preparation.isSplitTurn && prefixMessagesForSummary.length > 0) {
if (preparation.isSplitTurn && turnPrefixMessages.length > 0) {
const prefixSummary = await summarizeInStages({
messages: prefixMessagesForSummary,
messages: turnPrefixMessages,
model,
apiKey,
signal,
@@ -1218,8 +993,6 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void {
export const __testing = {
collectToolFailures,
formatToolFailuresSection,
trimToolResultsForSummarization,
restoreOriginalToolResultsForKeptMessages,
splitPreservedRecentTurns,
formatPreservedTurnsSection,
buildCompactionStructureInstructions,

View File

@@ -488,143 +488,3 @@ describe("stripToolResultDetails", () => {
expect(out).toBe(input);
});
});
describe("post-compaction orphaned tool_result removal (#15691)", () => {
it("drops orphaned tool_result blocks left after compaction removes tool_use messages", () => {
const input = castAgentMessages([
{
role: "assistant",
content: [{ type: "text", text: "Here is a summary of our earlier conversation..." }],
},
{
role: "toolResult",
toolCallId: "toolu_compacted_1",
toolName: "Read",
content: [{ type: "text", text: "file contents" }],
isError: false,
},
{
role: "toolResult",
toolCallId: "toolu_compacted_2",
toolName: "exec",
content: [{ type: "text", text: "command output" }],
isError: false,
},
{ role: "user", content: "now do something else" },
{
role: "assistant",
content: [
{ type: "text", text: "I'll read that file" },
{ type: "toolCall", id: "toolu_active_1", name: "Read", arguments: { path: "foo.ts" } },
],
},
{
role: "toolResult",
toolCallId: "toolu_active_1",
toolName: "Read",
content: [{ type: "text", text: "actual content" }],
isError: false,
},
]);
const result = repairToolUseResultPairing(input);
expect(result.droppedOrphanCount).toBe(2);
const toolResults = result.messages.filter((message) => message.role === "toolResult");
expect(toolResults).toHaveLength(1);
expect((toolResults[0] as { toolCallId?: string }).toolCallId).toBe("toolu_active_1");
expect(result.messages.map((message) => message.role)).toEqual([
"assistant",
"user",
"assistant",
"toolResult",
]);
});
it("handles synthetic tool_result from interrupted request after compaction", () => {
const input = castAgentMessages([
{
role: "assistant",
content: [{ type: "text", text: "Compaction summary of previous conversation." }],
},
{
role: "toolResult",
toolCallId: "toolu_interrupted",
toolName: "unknown",
content: [
{
type: "text",
text: "[openclaw] missing tool result in session history; inserted synthetic error result for transcript repair.",
},
],
isError: true,
},
{ role: "user", content: "continue please" },
]);
const result = repairToolUseResultPairing(input);
expect(result.droppedOrphanCount).toBe(1);
expect(result.messages.some((message) => message.role === "toolResult")).toBe(false);
expect(result.messages.map((message) => message.role)).toEqual(["assistant", "user"]);
});
it("preserves valid tool_use/tool_result pairs while removing orphans", () => {
const input = castAgentMessages([
{
role: "assistant",
content: [
{ type: "toolCall", id: "toolu_valid", name: "Read", arguments: { path: "a.ts" } },
],
},
{
role: "toolResult",
toolCallId: "toolu_valid",
toolName: "Read",
content: [{ type: "text", text: "content of a.ts" }],
isError: false,
},
{ role: "user", content: "thanks, what about b.ts?" },
{
role: "toolResult",
toolCallId: "toolu_gone",
toolName: "Read",
content: [{ type: "text", text: "content of old file" }],
isError: false,
},
{
role: "assistant",
content: [{ type: "text", text: "Let me check b.ts" }],
},
]);
const result = repairToolUseResultPairing(input);
expect(result.droppedOrphanCount).toBe(1);
const toolResults = result.messages.filter((message) => message.role === "toolResult");
expect(toolResults).toHaveLength(1);
expect((toolResults[0] as { toolCallId?: string }).toolCallId).toBe("toolu_valid");
});
it("returns original array when no orphans exist", () => {
const input = castAgentMessages([
{
role: "assistant",
content: [{ type: "toolCall", id: "toolu_1", name: "Read", arguments: { path: "x.ts" } }],
},
{
role: "toolResult",
toolCallId: "toolu_1",
toolName: "Read",
content: [{ type: "text", text: "ok" }],
isError: false,
},
{ role: "user", content: "good" },
]);
const result = repairToolUseResultPairing(input);
expect(result.droppedOrphanCount).toBe(0);
expect(result.messages).toStrictEqual(input);
});
});

View File

@@ -396,7 +396,7 @@ export function registerConfigCli(program: Command) {
const cmd = program
.command("config")
.description(
"Non-interactive config helpers (get/set/unset/file/validate). Run without subcommand for the setup wizard.",
"Non-interactive config helpers (get/set/unset/file/validate). Run without subcommand for guided setup.",
)
.addHelpText(
"after",
@@ -405,7 +405,7 @@ export function registerConfigCli(program: Command) {
)
.option(
"--section <section>",
"Configure wizard sections (repeatable). Use with no subcommand.",
"Configuration sections for guided setup (repeatable). Use with no subcommand.",
(value: string, previous: string[]) => [...previous, value],
[] as string[],
)

View File

@@ -56,7 +56,7 @@ const coreEntries: CoreCliEntry[] = [
commands: [
{
name: "onboard",
description: "Interactive setup wizard for gateway, workspace, and skills",
description: "Interactive onboarding for gateway, workspace, and skills",
hasSubcommands: false,
},
],
@@ -70,7 +70,7 @@ const coreEntries: CoreCliEntry[] = [
{
name: "configure",
description:
"Interactive setup wizard for credentials, channels, gateway, and agent defaults",
"Interactive configuration for credentials, channels, gateway, and agent defaults",
hasSubcommands: false,
},
],
@@ -84,7 +84,7 @@ const coreEntries: CoreCliEntry[] = [
{
name: "config",
description:
"Non-interactive config helpers (get/set/unset/file/validate). Default: starts setup wizard.",
"Non-interactive config helpers (get/set/unset/file/validate). Default: starts guided setup.",
hasSubcommands: true,
},
],

View File

@@ -12,18 +12,18 @@ export const CORE_CLI_COMMAND_DESCRIPTORS = [
},
{
name: "onboard",
description: "Interactive setup wizard for gateway, workspace, and skills",
description: "Interactive onboarding for gateway, workspace, and skills",
hasSubcommands: false,
},
{
name: "configure",
description: "Interactive setup wizard for credentials, channels, gateway, and agent defaults",
description: "Interactive configuration for credentials, channels, gateway, and agent defaults",
hasSubcommands: false,
},
{
name: "config",
description:
"Non-interactive config helpers (get/set/unset/file/validate). Default: starts setup wizard.",
"Non-interactive config helpers (get/set/unset/file/validate). Default: starts guided setup.",
hasSubcommands: true,
},
{

View File

@@ -11,7 +11,7 @@ import { runCommandWithRuntime } from "../cli-utils.js";
export function registerConfigureCommand(program: Command) {
program
.command("configure")
.description("Interactive setup wizard for credentials, channels, gateway, and agent defaults")
.description("Interactive configuration for credentials, channels, gateway, and agent defaults")
.addHelpText(
"after",
() =>

View File

@@ -63,7 +63,7 @@ function pickOnboardProviderAuthOptionValues(
export function registerOnboardCommand(program: Command) {
const command = program
.command("onboard")
.description("Interactive wizard to set up the gateway, workspace, and skills")
.description("Interactive onboarding for the gateway, workspace, and skills")
.addHelpText(
"after",
() =>
@@ -72,7 +72,7 @@ export function registerOnboardCommand(program: Command) {
.option("--workspace <dir>", "Agent workspace directory (default: ~/.openclaw/workspace)")
.option(
"--reset",
"Reset config + credentials + sessions before running wizard (workspace only with --reset-scope full)",
"Reset config + credentials + sessions before running onboard (workspace only with --reset-scope full)",
)
.option("--reset-scope <scope>", "Reset scope: config|config+creds+sessions|full")
.option("--non-interactive", "Run without prompts", false)
@@ -81,8 +81,8 @@ export function registerOnboardCommand(program: Command) {
"Acknowledge that agents are powerful and full system access is risky (required for --non-interactive)",
false,
)
.option("--flow <flow>", "Wizard flow: quickstart|advanced|manual")
.option("--mode <mode>", "Wizard mode: local|remote")
.option("--flow <flow>", "Onboard flow: quickstart|advanced|manual")
.option("--mode <mode>", "Onboard mode: local|remote")
.option("--auth-choice <choice>", `Auth: ${AUTH_CHOICE_HELP}`)
.option(
"--token-provider <id>",

View File

@@ -20,9 +20,9 @@ export function registerSetupCommand(program: Command) {
"--workspace <dir>",
"Agent workspace directory (default: ~/.openclaw/workspace; stored as agents.defaults.workspace)",
)
.option("--wizard", "Run the interactive onboarding wizard", false)
.option("--non-interactive", "Run the wizard without prompts", false)
.option("--mode <mode>", "Wizard mode: local|remote")
.option("--wizard", "Run interactive onboarding", false)
.option("--non-interactive", "Run onboarding without prompts", false)
.option("--mode <mode>", "Onboard mode: local|remote")
.option("--remote-url <url>", "Remote Gateway WebSocket URL")
.option("--remote-token <token>", "Remote Gateway token (optional)")
.action(async (opts, command) => {

View File

@@ -238,7 +238,7 @@ export async function applyAuthChoicePluginProvider(
const provider = resolveProviderMatch(providers, options.providerId);
if (!provider) {
await params.prompter.note(
`${options.label} auth plugin is not available. Enable it and re-run the wizard.`,
`${options.label} auth plugin is not available. Enable it and re-run onboarding.`,
options.label,
);
return { config: nextConfig };

View File

@@ -3,11 +3,14 @@ import { readLatestAssistantReply } from "../../agents/tools/agent-step.js";
import { SILENT_REPLY_TOKEN } from "../../auto-reply/tokens.js";
import { callGateway } from "../../gateway/call.js";
const FAST_TEST_MODE = process.env.OPENCLAW_TEST_FAST === "1";
const CRON_SUBAGENT_WAIT_MIN_MS = FAST_TEST_MODE ? 10 : 30_000;
const CRON_SUBAGENT_FINAL_REPLY_GRACE_MS = FAST_TEST_MODE ? 50 : 5_000;
const CRON_SUBAGENT_GRACE_POLL_MS = FAST_TEST_MODE ? 8 : 200;
function resolveCronSubagentTimings() {
const fastTestMode = process.env.OPENCLAW_TEST_FAST === "1";
return {
waitMinMs: fastTestMode ? 10 : 30_000,
finalReplyGraceMs: fastTestMode ? 50 : 5_000,
gracePollMs: fastTestMode ? 8 : 200,
};
}
const SUBAGENT_FOLLOWUP_HINTS = [
"subagent spawned",
@@ -121,8 +124,9 @@ export async function waitForDescendantSubagentSummary(params: {
timeoutMs: number;
observedActiveDescendants?: boolean;
}): Promise<string | undefined> {
const timings = resolveCronSubagentTimings();
const initialReply = params.initialReply?.trim();
const deadline = Date.now() + Math.max(CRON_SUBAGENT_WAIT_MIN_MS, Math.floor(params.timeoutMs));
const deadline = Date.now() + Math.max(timings.waitMinMs, Math.floor(params.timeoutMs));
// Snapshot the currently active descendant run IDs.
const getActiveRuns = () =>
@@ -166,8 +170,8 @@ export async function waitForDescendantSubagentSummary(params: {
// --- Grace period: wait for the cron agent's synthesis ---
// After the subagent announces fire and the cron agent processes them, it
// produces a new assistant message. Poll briefly (bounded by
// CRON_SUBAGENT_FINAL_REPLY_GRACE_MS) to capture that synthesis.
const gracePeriodDeadline = Math.min(Date.now() + CRON_SUBAGENT_FINAL_REPLY_GRACE_MS, deadline);
// finalReplyGraceMs) to capture that synthesis.
const gracePeriodDeadline = Math.min(Date.now() + timings.finalReplyGraceMs, deadline);
const resolveUsableLatestReply = async () => {
const latest = (await readLatestAssistantReply({ sessionKey: params.sessionKey }))?.trim();
@@ -186,7 +190,7 @@ export async function waitForDescendantSubagentSummary(params: {
if (latest) {
return latest;
}
await new Promise<void>((resolve) => setTimeout(resolve, CRON_SUBAGENT_GRACE_POLL_MS));
await new Promise<void>((resolve) => setTimeout(resolve, timings.gracePollMs));
}
// Final read after grace period expires.

View File

@@ -50,6 +50,13 @@ describe("resolveProviderAuths key normalization", () => {
process.env.HOME = base;
process.env.USERPROFILE = base;
if (process.platform === "win32") {
const match = base.match(/^([A-Za-z]:)(.*)$/);
if (match) {
process.env.HOMEDRIVE = match[1];
process.env.HOMEPATH = match[2] || "\\";
}
}
delete process.env.OPENCLAW_HOME;
process.env.OPENCLAW_STATE_DIR = path.join(base, ".openclaw");
for (const [key, value] of Object.entries(env)) {

View File

@@ -73,10 +73,13 @@ describe("loadEnabledBundleMcpConfig", () => {
cfg: config,
});
const resolvedServerPath = await fs.realpath(serverPath);
const loadedServerPath = loaded.config.mcpServers.bundleProbe?.args?.[0];
expect(loaded.diagnostics).toEqual([]);
expect(loaded.config.mcpServers.bundleProbe?.command).toBe("node");
expect(loaded.config.mcpServers.bundleProbe?.args).toEqual([resolvedServerPath]);
expect(loaded.config.mcpServers.bundleProbe?.args).toHaveLength(1);
expect(loadedServerPath).toBeDefined();
expect(await fs.realpath(loadedServerPath as string)).toBe(resolvedServerPath);
} finally {
env.restore();
}

View File

@@ -45,21 +45,22 @@ describe("marketplace plugins", () => {
const { listMarketplacePlugins } = await import("./marketplace.js");
const result = await listMarketplacePlugins({ marketplace: rootDir });
expect(result).toEqual({
ok: true,
sourceLabel: expect.stringContaining(".claude-plugin/marketplace.json"),
manifest: {
name: "Example Marketplace",
version: "1.0.0",
plugins: [
{
name: "frontend-design",
version: "0.1.0",
description: "Design system bundle",
source: { kind: "path", path: "./plugins/frontend-design" },
},
],
},
expect(result.ok).toBe(true);
if (!result.ok) {
throw new Error("expected marketplace listing to succeed");
}
expect(result.sourceLabel.replaceAll("\\", "/")).toContain(".claude-plugin/marketplace.json");
expect(result.manifest).toEqual({
name: "Example Marketplace",
version: "1.0.0",
plugins: [
{
name: "frontend-design",
version: "0.1.0",
description: "Design system bundle",
source: { kind: "path", path: "./plugins/frontend-design" },
},
],
});
});
});