docs: typography hygiene across 5 pages

Replaced 75 typography characters (curly quotes, apostrophes, em/en
dashes, non-breaking hyphens) with ASCII equivalents per
docs/CLAUDE.md heading and content hygiene rules.

- docs/plugins/skill-workshop.md: 15 chars
- docs/gateway/pairing.md: 15 chars
- docs/gateway/configuration.md: 15 chars
- docs/concepts/oauth.md: 15 chars
- docs/channels/bluebubbles.md: 15 chars
This commit is contained in:
Vincent Koc
2026-05-05 20:14:18 -07:00
parent c73f774b9b
commit 861a593921
5 changed files with 59 additions and 59 deletions

View File

@@ -381,7 +381,7 @@ BlueBubbles supports advanced message actions when enabled in config:
- **reply**: Reply to a specific message (`messageId`, `text`, `to`).
- **sendWithEffect**: Send with iMessage effect (`text`, `to`, `effectId`).
- **renameGroup**: Rename a group chat (`chatGuid`, `displayName`).
- **setGroupIcon**: Set a group chat's icon/photo (`chatGuid`, `media`) flaky on macOS 26 Tahoe (API may return success but the icon does not sync).
- **setGroupIcon**: Set a group chat's icon/photo (`chatGuid`, `media`) - flaky on macOS 26 Tahoe (API may return success but the icon does not sync).
- **addParticipant**: Add someone to a group (`chatGuid`, `address`).
- **removeParticipant**: Remove someone from a group (`chatGuid`, `address`).
- **leaveGroup**: Leave a group chat (`chatGuid`).
@@ -412,12 +412,12 @@ See [Configuration](/gateway/configuration) for template variables.
## Coalescing split-send DMs (command + URL in one composition)
When a user types a command and a URL together in iMessage e.g. `Dump https://example.com/article` Apple splits the send into **two separate webhook deliveries**:
When a user types a command and a URL together in iMessage - e.g. `Dump https://example.com/article` - Apple splits the send into **two separate webhook deliveries**:
1. A text message (`"Dump"`).
2. A URL-preview balloon (`"https://..."`) with OG-preview images as attachments.
The two webhooks arrive at OpenClaw ~0.8-2.0 s apart on most setups. Without coalescing, the agent receives the command alone on turn 1, replies (often "send me the URL"), and only sees the URL on turn 2 at which point the command context is already lost.
The two webhooks arrive at OpenClaw ~0.8-2.0 s apart on most setups. Without coalescing, the agent receives the command alone on turn 1, replies (often "send me the URL"), and only sees the URL on turn 2 - at which point the command context is already lost.
`channels.bluebubbles.coalesceSameSenderDms` opts a DM into merging consecutive same-sender webhooks into a single agent turn. Group chats continue to key per-message so multi-user turn structure is preserved.
@@ -446,7 +446,7 @@ The two webhooks arrive at OpenClaw ~0.8-2.0 s apart on most setups. Without coa
}
```
With the flag on and no explicit `messages.inbound.byChannel.bluebubbles`, the debounce window widens to **2500 ms** (the default for non-coalescing is 500 ms). The wider window is required Apple's split-send cadence of 0.8-2.0 s does not fit in the tighter default.
With the flag on and no explicit `messages.inbound.byChannel.bluebubbles`, the debounce window widens to **2500 ms** (the default for non-coalescing is 500 ms). The wider window is required - Apple's split-send cadence of 0.8-2.0 s does not fit in the tighter default.
To tune the window yourself:
@@ -467,7 +467,7 @@ The two webhooks arrive at OpenClaw ~0.8-2.0 s apart on most setups. Without coa
</Tab>
<Tab title="Trade-offs">
- **Added latency for DM control commands.** With the flag on, DM control-command messages (like `Dump`, `Save`, etc.) now wait up to the debounce window before dispatching, in case a payload webhook is coming. Group-chat commands keep instant dispatch.
- **Merged output is bounded** merged text caps at 4000 chars with an explicit `…[truncated]` marker; attachments cap at 20; source entries cap at 10 (first-plus-latest retained beyond that). Every source `messageId` still reaches inbound-dedupe so a later MessagePoller replay of any individual event is recognized as a duplicate.
- **Merged output is bounded** - merged text caps at 4000 chars with an explicit `…[truncated]` marker; attachments cap at 20; source entries cap at 10 (first-plus-latest retained beyond that). Every source `messageId` still reaches inbound-dedupe so a later MessagePoller replay of any individual event is recognized as a duplicate.
- **Opt-in, per-channel.** Other channels (Telegram, WhatsApp, Slack, …) are unaffected.
</Tab>
@@ -494,7 +494,7 @@ If the flag is on and split-sends still arrive as two turns, check each layer:
grep coalesceSameSenderDms ~/.openclaw/openclaw.json
```
Then `openclaw gateway restart` the flag is read at debouncer-registry creation.
Then `openclaw gateway restart` - the flag is read at debouncer-registry creation.
</Accordion>
<Accordion title="Debounce window wide enough for your setup">
@@ -508,13 +508,13 @@ If the flag is on and split-sends still arrive as two turns, check each layer:
</Accordion>
<Accordion title="Session JSONL timestamps ≠ webhook arrival">
Session event timestamps (`~/.openclaw/agents/<id>/sessions/*.jsonl`) reflect when the gateway hands a message to the agent, **not** when the webhook arrived. A queued-second message tagged `[Queued messages while agent was busy]` means the first turn was still running when the second webhook arrived the coalesce bucket had already flushed. Tune the window against the BB server log, not the session log.
Session event timestamps (`~/.openclaw/agents/<id>/sessions/*.jsonl`) reflect when the gateway hands a message to the agent, **not** when the webhook arrived. A queued-second message tagged `[Queued messages while agent was busy]` means the first turn was still running when the second webhook arrived - the coalesce bucket had already flushed. Tune the window against the BB server log, not the session log.
</Accordion>
<Accordion title="Memory pressure slowing reply dispatch">
On smaller machines (8 GB), agent turns can take long enough that the coalesce bucket flushes before the reply completes, and the URL lands as a queued second turn. Check `memory_pressure` and `ps -o rss -p $(pgrep openclaw-gateway)`; if the gateway is over ~500 MB RSS and the compressor is active, close other heavy processes or bump to a larger host.
</Accordion>
<Accordion title="Reply-quote sends are a different path">
If the user tapped `Dump` as a **reply** to an existing URL-balloon (iMessage shows a "1 Reply" badge on the Dump bubble), the URL lives in `replyToBody`, not in a second webhook. Coalescing does not apply that's a skill/prompt concern, not a debouncer concern.
If the user tapped `Dump` as a **reply** to an existing URL-balloon (iMessage shows a "1 Reply" badge on the Dump bubble), the URL lives in `replyToBody`, not in a second webhook. Coalescing does not apply - that's a skill/prompt concern, not a debouncer concern.
</Accordion>
</AccordionGroup>
@@ -617,15 +617,15 @@ When the same handle has both an iMessage and an SMS chat on the Mac (for exampl
- Edit/unsend require macOS 13+ and a compatible BlueBubbles server version. On macOS 26 (Tahoe), edit is currently broken due to private API changes.
- Group icon updates can be flaky on macOS 26 (Tahoe): the API may return success but the new icon does not sync.
- OpenClaw auto-hides known-broken actions based on the BlueBubbles server's macOS version. If edit still appears on macOS 26 (Tahoe), disable it manually with `channels.bluebubbles.actions.edit=false`.
- `coalesceSameSenderDms` enabled but split-sends (e.g. `Dump` + URL) still arrive as two turns: see the [split-send coalescing troubleshooting](#split-send-coalescing-troubleshooting) checklist common causes are too-tight debounce window, session-log timestamps misread as webhook arrival, or a reply-quote send (which uses `replyToBody`, not a second webhook).
- `coalesceSameSenderDms` enabled but split-sends (e.g. `Dump` + URL) still arrive as two turns: see the [split-send coalescing troubleshooting](#split-send-coalescing-troubleshooting) checklist - common causes are too-tight debounce window, session-log timestamps misread as webhook arrival, or a reply-quote send (which uses `replyToBody`, not a second webhook).
- For status/health info: `openclaw status --all` or `openclaw status --deep`.
For general channel workflow reference, see [Channels](/channels) and the [Plugins](/tools/plugin) guide.
## Related
- [Channel Routing](/channels/channel-routing) session routing for messages
- [Channels Overview](/channels) all supported channels
- [Groups](/channels/groups) group chat behavior and mention gating
- [Pairing](/channels/pairing) DM authentication and pairing flow
- [Security](/gateway/security) access model and hardening
- [Channel Routing](/channels/channel-routing) - session routing for messages
- [Channels Overview](/channels) - all supported channels
- [Groups](/channels/groups) - group chat behavior and mention gating
- [Pairing](/channels/pairing) - DM authentication and pairing flow
- [Security](/gateway/security) - access model and hardening

View File

@@ -8,7 +8,7 @@ read_when:
title: "OAuth"
---
OpenClaw supports subscription auth via OAuth for providers that offer it
OpenClaw supports "subscription auth" via OAuth for providers that offer it
(notably **OpenAI Codex (ChatGPT OAuth)**). For Anthropic, the practical split
is now:
@@ -25,7 +25,7 @@ For Anthropic in production, API key auth is the safer recommended path.
- where tokens are **stored** (and why)
- how to handle **multiple accounts** (profiles + per-session overrides)
OpenClaw also supports **provider plugins** that ship their own OAuth or APIkey
OpenClaw also supports **provider plugins** that ship their own OAuth or API-key
flows. Run them via:
```bash
@@ -38,7 +38,7 @@ OAuth providers commonly mint a **new refresh token** during login/refresh flows
Practical symptom:
- you log in via OpenClaw _and_ via Claude Code / Codex CLI → one of them randomly gets logged out later
- you log in via OpenClaw _and_ via Claude Code / Codex CLI → one of them randomly gets "logged out" later
To reduce that, OpenClaw treats `auth-profiles.json` as a **token sink**:
@@ -105,7 +105,7 @@ Claude login on the host, onboarding/configure can reuse it directly.
## OAuth exchange (how login works)
OpenClaws interactive login flows are implemented in `@mariozechner/pi-ai` and wired into the wizards/commands.
OpenClaw's interactive login flows are implemented in `@mariozechner/pi-ai` and wired into the wizards/commands.
### Anthropic setup-token
@@ -125,7 +125,7 @@ Flow shape (PKCE):
1. generate PKCE verifier/challenge + random `state`
2. open `https://auth.openai.com/oauth/authorize?...`
3. try to capture callback on `http://127.0.0.1:1455/auth/callback`
4. if callback cant bind (or youre remote/headless), paste the redirect URL/code
4. if callback can't bind (or you're remote/headless), paste the redirect URL/code
5. exchange at `https://auth.openai.com/oauth/token`
6. extract `accountId` from the access token and store `{ access, refresh, expires, accountId }`
@@ -156,7 +156,7 @@ Two patterns:
### 1) Preferred: separate agents
If you want personal and work to never interact, use isolated agents (separate sessions + credentials + workspace):
If you want "personal" and "work" to never interact, use isolated agents (separate sessions + credentials + workspace):
```bash
openclaw agents add work
@@ -189,6 +189,6 @@ Related docs:
## Related
- [Authentication](/gateway/authentication) model provider auth overview
- [Secrets](/gateway/secrets) credential storage and SecretRef
- [Configuration Reference](/gateway/configuration-reference#auth-storage) auth config keys
- [Authentication](/gateway/authentication) - model provider auth overview
- [Secrets](/gateway/secrets) - credential storage and SecretRef
- [Configuration Reference](/gateway/configuration-reference#auth-storage) - auth config keys

View File

@@ -103,16 +103,16 @@ candidate contains redacted secret placeholders such as `***`.
<Accordion title="Set up a channel (WhatsApp, Telegram, Discord, etc.)">
Each channel has its own config section under `channels.<provider>`. See the dedicated channel page for setup steps:
- [WhatsApp](/channels/whatsapp) `channels.whatsapp`
- [Telegram](/channels/telegram) `channels.telegram`
- [Discord](/channels/discord) `channels.discord`
- [Feishu](/channels/feishu) `channels.feishu`
- [Google Chat](/channels/googlechat) `channels.googlechat`
- [Microsoft Teams](/channels/msteams) `channels.msteams`
- [Slack](/channels/slack) `channels.slack`
- [Signal](/channels/signal) `channels.signal`
- [iMessage](/channels/imessage) `channels.imessage`
- [Mattermost](/channels/mattermost) `channels.mattermost`
- [WhatsApp](/channels/whatsapp) - `channels.whatsapp`
- [Telegram](/channels/telegram) - `channels.telegram`
- [Discord](/channels/discord) - `channels.discord`
- [Feishu](/channels/feishu) - `channels.feishu`
- [Google Chat](/channels/googlechat) - `channels.googlechat`
- [Microsoft Teams](/channels/msteams) - `channels.msteams`
- [Slack](/channels/slack) - `channels.slack`
- [Signal](/channels/signal) - `channels.signal`
- [iMessage](/channels/imessage) - `channels.imessage`
- [Mattermost](/channels/mattermost) - `channels.mattermost`
All channels share the same DM policy pattern:
@@ -329,7 +329,7 @@ candidate contains redacted secret placeholders such as `***`.
}
```
Build the image first from a source checkout run `scripts/sandbox-setup.sh`, or from an npm install see the inline `docker build` command in [Sandboxing § Images and setup](/gateway/sandboxing#images-and-setup).
Build the image first - from a source checkout run `scripts/sandbox-setup.sh`, or from an npm install see the inline `docker build` command in [Sandboxing § Images and setup](/gateway/sandboxing#images-and-setup).
See [Sandboxing](/gateway/sandboxing) for the full guide and [full reference](/gateway/config-agents#agentsdefaultssandbox) for all options.
@@ -531,7 +531,7 @@ candidate contains redacted secret placeholders such as `***`.
## Config hot reload
The Gateway watches `~/.openclaw/openclaw.json` and applies changes automatically no manual restart needed for most settings.
The Gateway watches `~/.openclaw/openclaw.json` and applies changes automatically - no manual restart needed for most settings.
Direct file edits are treated as untrusted until they validate. The watcher waits
for editor temp-write/rename churn to settle, reads the final file, and rejects
@@ -550,7 +550,7 @@ for the checklist.
| Mode | Behavior |
| ---------------------- | --------------------------------------------------------------------------------------- |
| **`hybrid`** (default) | Hot-applies safe changes instantly. Automatically restarts for critical ones. |
| **`hot`** | Hot-applies safe changes only. Logs a warning when a restart is needed you handle it. |
| **`hot`** | Hot-applies safe changes only. Logs a warning when a restart is needed - you handle it. |
| **`restart`** | Restarts the Gateway on any config change, safe or not. |
| **`off`** | Disables file watching. Changes take effect on the next manual restart. |
@@ -568,7 +568,7 @@ Most fields hot-apply without downtime. In `hybrid` mode, restart-required chang
| Category | Fields | Restart needed? |
| ------------------- | ----------------------------------------------------------------- | --------------- |
| Channels | `channels.*`, `web` (WhatsApp) all built-in and plugin channels | No |
| Channels | `channels.*`, `web` (WhatsApp) - all built-in and plugin channels | No |
| Agent & models | `agent`, `agents`, `models`, `routing` | No |
| Automation | `hooks`, `cron`, `agent.heartbeat` | No |
| Sessions & messages | `session`, `messages` | No |
@@ -578,7 +578,7 @@ Most fields hot-apply without downtime. In `hybrid` mode, restart-required chang
| Infrastructure | `discovery`, `canvasHost`, `plugins` | **Yes** |
<Note>
`gateway.reload` and `gateway.remote` are exceptions changing them does **not** trigger a restart.
`gateway.reload` and `gateway.remote` are exceptions - changing them does **not** trigger a restart.
</Note>
### Reload planning

View File

@@ -27,8 +27,8 @@ Only clients that explicitly call `node.pair.*` use this flow.
1. A node connects to the Gateway WS and requests pairing.
2. The Gateway stores a **pending request** and emits `node.pair.requested`.
3. You approve or reject the request (CLI or UI).
4. On approval, the Gateway issues a **new token** (tokens are rotated on repair).
5. The node reconnects using the token and is now paired.
4. On approval, the Gateway issues a **new token** (tokens are rotated on re-pair).
5. The node reconnects using the token and is now "paired".
Pending requests expire automatically after **5 minutes**.
@@ -49,17 +49,17 @@ openclaw nodes rename --node <id|name|ip> --name "Living Room iPad"
Events:
- `node.pair.requested` emitted when a new pending request is created.
- `node.pair.resolved` emitted when a request is approved/rejected/expired.
- `node.pair.requested` - emitted when a new pending request is created.
- `node.pair.resolved` - emitted when a request is approved/rejected/expired.
Methods:
- `node.pair.request` create or reuse a pending request.
- `node.pair.list` list pending + paired nodes (`operator.pairing`).
- `node.pair.approve` approve a pending request (issues token).
- `node.pair.reject` reject a pending request.
- `node.pair.remove` remove a stale paired node entry.
- `node.pair.verify` verify `{ nodeId, token }`.
- `node.pair.request` - create or reuse a pending request.
- `node.pair.list` - list pending + paired nodes (`operator.pairing`).
- `node.pair.approve` - approve a pending request (issues token).
- `node.pair.reject` - reject a pending request.
- `node.pair.remove` - remove a stale paired node entry.
- `node.pair.verify` - verify `{ nodeId, token }`.
Notes:
@@ -120,7 +120,7 @@ The macOS app can optionally attempt a **silent approval** when:
- the request is marked `silent`, and
- the app can verify an SSH connection to the gateway host using the same user.
If silent approval fails, it falls back to the normal Approve/Reject prompt.
If silent approval fails, it falls back to the normal "Approve/Reject" prompt.
## Trusted-CIDR device auto-approval
@@ -159,7 +159,7 @@ to trusted non-browser local reconnects that already proved possession of local
or shared credentials, including same-host native app reconnects after OS
version metadata changes. Browser/Control UI clients and remote clients still
use the explicit re-approval flow. Scope upgrades (read to write/admin) and
public key changes are **not** eligible for metadata-upgrade auto-approval
public key changes are **not** eligible for metadata-upgrade auto-approval -
they stay as explicit re-approval requests.
## QR pairing helpers
@@ -199,7 +199,7 @@ Security notes:
- The transport is **stateless**; it does not store membership.
- If the Gateway is offline or pairing is disabled, nodes cannot pair.
- If the Gateway is in remote mode, pairing still happens against the remote Gateways store.
- If the Gateway is in remote mode, pairing still happens against the remote Gateway's store.
## Related

View File

@@ -38,7 +38,7 @@ Skill Workshop is useful when the agent learns a procedure such as:
It is not intended for:
- facts like the user likes blue
- facts like "the user likes blue"
- broad autobiographical memory
- raw transcript archiving
- secrets, credentials, or hidden prompt text
@@ -217,7 +217,7 @@ The reviewer has no tools:
- `toolsAllow: []`
- `disableMessageTool: true`
The reviewer returns either `{ "action": "none" }` or one proposal. The `action` field is `create`, `append`, or `replace` prefer `append`/`replace` when a relevant skill already exists; use `create` only when no existing skill fits.
The reviewer returns either `{ "action": "none" }` or one proposal. The `action` field is `create`, `append`, or `replace` - prefer `append`/`replace` when a relevant skill already exists; use `create` only when no existing skill fits.
Example `create`:
@@ -579,12 +579,12 @@ warning/debug message and skips that review pass.
Use Skill Workshop when the user says:
- next time, do X
- from now on, prefer Y
- make sure to verify Z
- save this as a workflow
- this took a while; remember the process
- update the local skill for this
- "next time, do X"
- "from now on, prefer Y"
- "make sure to verify Z"
- "save this as a workflow"
- "this took a while; remember the process"
- "update the local skill for this"
Good skill text: