diff --git a/docs/concepts/markdown-formatting.md b/docs/concepts/markdown-formatting.md
index d1b8e8ca5d5..e54833cf9fb 100644
--- a/docs/concepts/markdown-formatting.md
+++ b/docs/concepts/markdown-formatting.md
@@ -39,14 +39,14 @@ stay consistent across channels.
Input Markdown:
```markdown
-Hello **world** — see [docs](https://docs.openclaw.ai).
+Hello **world** - see [docs](https://docs.openclaw.ai).
```
IR (schematic):
```json
{
- "text": "Hello world — see docs.",
+ "text": "Hello world - see docs.",
"styles": [{ "start": 6, "end": 11, "style": "bold" }],
"links": [{ "start": 19, "end": 23, "href": "https://docs.openclaw.ai" }]
}
@@ -129,5 +129,11 @@ SPOILER style ranges. Other channels treat them as plain text.
## Related
-- [Streaming and chunking](/concepts/streaming)
-- [System prompt](/concepts/system-prompt)
+
+
+ Outbound streaming behavior, chunk boundaries, and channel-specific delivery.
+
+
+ What the model sees before the conversation, including injected workspace files.
+
+
diff --git a/docs/concepts/presence.md b/docs/concepts/presence.md
index 3c6b1f14098..0d8f9c7261c 100644
--- a/docs/concepts/presence.md
+++ b/docs/concepts/presence.md
@@ -7,12 +7,12 @@ read_when:
title: "Presence"
---
-OpenClaw “presence” is a lightweight, best‑effort view of:
+OpenClaw "presence" is a lightweight, best-effort view of:
- the **Gateway** itself, and
- **clients connected to the Gateway** (mac app, WebChat, CLI, etc.)
-Presence is used primarily to render the macOS app’s **Instances** tab and to
+Presence is used primarily to render the macOS app's **Instances** tab and to
provide quick operator visibility.
## Presence fields (what shows up)
@@ -20,12 +20,12 @@ provide quick operator visibility.
Presence entries are structured objects with fields like:
- `instanceId` (optional but strongly recommended): stable client identity (usually `connect.client.instanceId`)
-- `host`: human‑friendly host name
-- `ip`: best‑effort IP address
+- `host`: human-friendly host name
+- `ip`: best-effort IP address
- `version`: client version string
- `deviceFamily` / `modelIdentifier`: hardware hints
- `mode`: `ui`, `webchat`, `cli`, `backend`, `probe`, `test`, `node`, ...
-- `lastInputSeconds`: “seconds since last user input” (if known)
+- `lastInputSeconds`: "seconds since last user input" (if known)
- `reason`: `self`, `connect`, `node-connected`, `periodic`, ...
- `ts`: last update timestamp (ms since epoch)
@@ -35,7 +35,7 @@ Presence entries are produced by multiple sources and **merged**.
### 1) Gateway self entry
-The Gateway always seeds a “self” entry at startup so UIs show the gateway host
+The Gateway always seeds a "self" entry at startup so UIs show the gateway host
even before any clients connect.
### 2) WebSocket connect
@@ -45,7 +45,7 @@ Gateway upserts a presence entry for that connection.
#### Why one-off CLI commands do not show up
-The CLI often connects for short, one‑off commands. To avoid spamming the
+The CLI often connects for short, one-off commands. To avoid spamming the
Instances list, `client.mode === "cli"` is **not** turned into a presence entry.
### 3) `system-event` beacons
@@ -60,11 +60,11 @@ upserts a presence entry for that node (same flow as other WS clients).
## Merge + dedupe rules (why `instanceId` matters)
-Presence entries are stored in a single in‑memory map:
+Presence entries are stored in a single in-memory map:
- Entries are keyed by a **presence key**.
- The best key is a stable `instanceId` (from `connect.client.instanceId`) that survives restarts.
-- Keys are case‑insensitive.
+- Keys are case-insensitive.
If a client reconnects without a stable `instanceId`, it may show up as a
**duplicate** row.
@@ -81,7 +81,7 @@ This keeps the list fresh and avoids unbounded memory growth.
## Remote/tunnel caveat (loopback IPs)
When a client connects over an SSH tunnel / local port forward, the Gateway may
-see the remote address as `127.0.0.1`. To avoid overwriting a good client‑reported
+see the remote address as `127.0.0.1`. To avoid overwriting a good client-reported
IP, loopback remote addresses are ignored.
## Consumers
@@ -97,9 +97,21 @@ indicator (Active/Idle/Stale) based on the age of the last update.
- If you see duplicates:
- confirm clients send a stable `client.instanceId` in the handshake
- confirm periodic beacons use the same `instanceId`
- - check whether the connection‑derived entry is missing `instanceId` (duplicates are expected)
+ - check whether the connection-derived entry is missing `instanceId` (duplicates are expected)
## Related
-- [Typing indicators](/concepts/typing-indicators)
-- [Streaming and chunking](/concepts/streaming)
+
+
+ When typing indicators are sent and how to tune them.
+
+
+ Outbound streaming, chunking, and per-channel formatting.
+
+
+ Gateway components and the WebSocket protocol that drives presence updates.
+
+
+ The wire protocol for `connect`, `system-event`, and `system-presence`.
+
+
diff --git a/docs/concepts/typing-indicators.md b/docs/concepts/typing-indicators.md
index bc1c5874468..f69d0ccdf55 100644
--- a/docs/concepts/typing-indicators.md
+++ b/docs/concepts/typing-indicators.md
@@ -23,15 +23,15 @@ When `agents.defaults.typingMode` is **unset**, OpenClaw keeps the legacy behavi
Set `agents.defaults.typingMode` to one of:
-- `never` — no typing indicator, ever.
-- `instant` — start typing **as soon as the model loop begins**, even if the run
+- `never` - no typing indicator, ever.
+- `instant` - start typing **as soon as the model loop begins**, even if the run
later returns only the silent reply token.
-- `thinking` — start typing on the **first reasoning delta** (requires
+- `thinking` - start typing on the **first reasoning delta** (requires
`reasoningLevel: "stream"` for the run).
-- `message` — start typing on the **first non-silent text delta** (ignores
+- `message` - start typing on the **first non-silent text delta** (ignores
the `NO_REPLY` silent token).
-Order of “how early it fires”:
+Order of "how early it fires":
`never` → `message` → `thinking` → `instant`
## Configuration
@@ -58,11 +58,11 @@ You can override mode or cadence per session:
## Notes
-- `message` mode won’t show typing for silent-only replies when the whole
+- `message` mode won't show typing for silent-only replies when the whole
payload is the exact silent token (for example `NO_REPLY` / `no_reply`,
matched case-insensitively).
- `thinking` only fires if the run streams reasoning (`reasoningLevel: "stream"`).
- If the model doesn’t emit reasoning deltas, typing won’t start.
+ If the model doesn't emit reasoning deltas, typing won't start.
- Heartbeat typing is a liveness signal for the resolved delivery target. It
starts at heartbeat run start instead of following `message` or `thinking`
stream timing. Set `typingMode: "never"` to disable it.
@@ -74,5 +74,11 @@ You can override mode or cadence per session:
## Related
-- [Presence](/concepts/presence)
-- [Streaming and chunking](/concepts/streaming)
+
+
+ How the Gateway tracks connected clients and surfaces them in the macOS Instances tab.
+
+
+ Outbound streaming behavior, chunk boundaries, and channel-specific delivery.
+
+