Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
139
docs/concepts/markdown-formatting.md
Normal file
139
docs/concepts/markdown-formatting.md
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
summary: "Markdown formatting pipeline for outbound channels"
|
||||
read_when:
|
||||
- You are changing markdown formatting or chunking for outbound channels
|
||||
- You are adding a new channel formatter or style mapping
|
||||
- You are debugging formatting regressions across channels
|
||||
title: "Markdown formatting"
|
||||
---
|
||||
|
||||
OpenClaw converts outbound Markdown into a shared intermediate representation
|
||||
(IR) before rendering channel-specific output. The IR keeps plain text plus
|
||||
style/link spans, so one parse step feeds every channel and chunking never
|
||||
splits formatting mid-span.
|
||||
|
||||
## Pipeline
|
||||
|
||||
1. **Parse Markdown into IR** (`markdownToIR`) - plain text plus style spans
|
||||
(bold, italic, strikethrough, code, code block, spoiler, blockquote,
|
||||
heading 1-6) and link spans. Offsets are UTF-16 code units so Signal style
|
||||
ranges align with its API directly. Tables parse only when the channel
|
||||
opts into a table mode.
|
||||
2. **Chunk the IR** (`chunkMarkdownIR` / `renderMarkdownIRChunksWithinLimit`)
|
||||
- splitting happens on IR text before rendering, so inline styles and
|
||||
links are sliced per chunk instead of breaking across a boundary.
|
||||
3. **Render per channel** (`renderMarkdownWithMarkers`) - a style-marker map
|
||||
turns spans into the channel's native markup.
|
||||
|
||||
| Channel | Renderer | Notes |
|
||||
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
|
||||
| Slack | mrkdwn tokens (`*bold*`, `_italic_`, `` `code` ``, code fences) | Links become `<url\|label>`; autolink disabled during parse to avoid double-linking |
|
||||
| Telegram | HTML tags (`<b>`, `<i>`, `<s>`, `<code>`, `<pre><code>`, `<a href>`, `<tg-spoiler>`) | Also supports rich-message tables and headings (`<h1>`-`<h6>`) when `richMessages` is on |
|
||||
| Signal | plain text + `text-style` ranges | Links render as `label (url)` when the label differs from the URL |
|
||||
| Discord, WhatsApp, iMessage, Microsoft Teams, and other channels | plain text | No IR-based styling; Markdown table conversion still runs via `convertMarkdownTables` |
|
||||
|
||||
## IR example
|
||||
|
||||
Input Markdown:
|
||||
|
||||
```markdown
|
||||
Hello **world** - see [docs](https://docs.openclaw.ai).
|
||||
```
|
||||
|
||||
IR (schematic):
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Hello world - see docs.",
|
||||
"styles": [{ "start": 6, "end": 11, "style": "bold" }],
|
||||
"links": [{ "start": 19, "end": 23, "href": "https://docs.openclaw.ai" }]
|
||||
}
|
||||
```
|
||||
|
||||
## Table handling
|
||||
|
||||
`markdown.tables` controls how a channel converts Markdown tables, per
|
||||
channel and optionally per account:
|
||||
|
||||
| Mode | Behavior |
|
||||
| --------- | ------------------------------------------------------------------------------------ |
|
||||
| `code` | Render as an aligned ASCII table inside a code block (fallback default) |
|
||||
| `bullets` | Convert each row into `label: value` bullet points |
|
||||
| `block` | Keep native tables where the transport supports them; falls back to `code` otherwise |
|
||||
| `off` | Disable table parsing; raw table text passes through unchanged |
|
||||
|
||||
Per-channel plugin defaults: Signal, WhatsApp, and Matrix default to
|
||||
`bullets`; Mattermost defaults to `off`; Telegram defaults to `block` (which
|
||||
resolves to `code` unless the account has `richMessages` enabled). Any
|
||||
channel without an explicit plugin default falls back to `code`.
|
||||
|
||||
```yaml
|
||||
channels:
|
||||
discord:
|
||||
markdown:
|
||||
tables: code
|
||||
accounts:
|
||||
work:
|
||||
markdown:
|
||||
tables: off
|
||||
```
|
||||
|
||||
## Chunking rules
|
||||
|
||||
- Chunk limits come from channel adapters/config and apply to IR text, not
|
||||
rendered output.
|
||||
- Fenced code blocks are kept as one block with a trailing newline so
|
||||
channels render the closing fence correctly.
|
||||
- List and blockquote prefixes are part of the IR text, so chunking never
|
||||
splits mid-prefix.
|
||||
- Inline styles never split across chunks; the renderer reopens an open
|
||||
style at the start of the next chunk.
|
||||
|
||||
See [Streaming and chunking](/concepts/streaming) for chunk-boundary and
|
||||
delivery behavior across channels.
|
||||
|
||||
## Link policy
|
||||
|
||||
- **Slack:** `[label](url)` -> `<url|label>`; bare URLs stay bare.
|
||||
- **Telegram:** `[label](url)` -> `<a href="url">label</a>` (HTML parse mode).
|
||||
- **Signal:** `[label](url)` -> `label (url)` unless the label already
|
||||
matches the URL.
|
||||
|
||||
## Spoilers
|
||||
|
||||
Spoiler markers (`||spoiler||`) are parsed for Signal (mapped to `SPOILER`
|
||||
style ranges) and Telegram (mapped to `<tg-spoiler>`). Other channels treat
|
||||
`||...||` as plain text.
|
||||
|
||||
## Adding or updating a channel formatter
|
||||
|
||||
1. **Parse once** with `markdownToIR(...)`, passing channel-appropriate
|
||||
options (`autolink`, `headingStyle`, `blockquotePrefix`, `tableMode`).
|
||||
2. **Render** with `renderMarkdownWithMarkers(...)` and a style-marker map (or
|
||||
custom style-range logic for transports like Signal).
|
||||
3. **Chunk** with `chunkMarkdownIR(...)` or
|
||||
`renderMarkdownIRChunksWithinLimit(...)` before rendering each chunk.
|
||||
4. **Wire the adapter** to call the new chunker and renderer from the
|
||||
outbound send path.
|
||||
5. **Test** with format tests plus an outbound delivery test if the channel
|
||||
chunks.
|
||||
|
||||
## Common gotchas
|
||||
|
||||
- Slack angle-bracket tokens (`<@U123>`, `<#C123>`, `<https://...>`) must
|
||||
survive escaping; raw HTML still needs to be escaped safely.
|
||||
- Telegram HTML requires escaping text outside tags to avoid broken markup.
|
||||
- Signal style ranges use UTF-16 offsets, not code-point offsets.
|
||||
- Preserve trailing newlines on fenced code blocks so the closing marker
|
||||
lands on its own line.
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Streaming and chunking" href="/concepts/streaming" icon="bars-staggered">
|
||||
Outbound streaming behavior, chunk boundaries, and channel-specific delivery.
|
||||
</Card>
|
||||
<Card title="System prompt" href="/concepts/system-prompt" icon="message-lines">
|
||||
What the model sees before the conversation, including injected workspace files.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
Reference in New Issue
Block a user