Concepts

Every front end — the Rust core, the npm package, the Go implementation and the CLI — takes the same inputs and produces the same output; they are held to the same test cases. This page covers what they share. The per-language pages list the exact names.

These docs track the main branch. Anything marked next release is not in the current release on npm, crates.io and Go yet; it ships with the next one.

Channels

A channel is where the output goes. Its name is the same string everywhere.

Name Goes to Limit
telegram-html Telegram parse_mode: "HTML". Nine tags (b i u s code pre a blockquote tg-spoiler), no headings or tables — a table goes out as a monospace block. 4,096
slack-markdown Slack markdown_text. Standard Markdown, which Slack converts itself; mdwire repairs, escapes a * meant as a character (\*) and splits. 12,000
github-markdown GitHub comments and PR bodies (GFM). A ~ or < meant as a character is escaped, and bold that GFM would not close (before a Korean particle) is written as <strong>. 65,536
notion-markdown Notion page body (Notion-flavored Markdown). Four heading levels; inline HTML Notion cannot draw is stripped, and an autolink becomes [url](url). 65,536 (not measured)
plain Plain text — every marker removed. The fallback. 12,000
html An HTML fragment for the browser, safe to set as innerHTML: text is escaped, inline tags from the source keep no attributes, and only allowed schemes become links. none

The limit is a character count on the rendered output. The sender can set its own (see options). For example, plain sent to Telegram has to fit 4,096.

Options

Option Default Meaning
limit the channel’s limit Characters per part. Values below 256 are raised to 256 — every part needs room to close and reopen markup. Streaming never splits, and neither does html.
html · line breaks br A line break inside a block: <br>, or space to let the browser fold it (for prose wrapped at 80 columns).
html · images link ![alt](url) as a link that loads nothing until clicked, or load for <img> (allowed schemes only).
html · schemes http, https, mailto Schemes allowed in links and image URLs. Giving a list replaces the default; it does not add to it.

The three html options apply only to the html channel.

The repair report

Rendering also counts what it had to do. The first five are repairs — how often the model broke its own formatting:

Field Counts
closed emphasis Emphasis still open at the end of a block, closed (**impact scope).
closed fence A code fence still open at the end of the document, closed.
reverted code span A backtick run with no partner, kept as text.
dropped marker A stray ** dropped (tail**, where text comes before it).
guessed pair Emphasis paired by a guess rather than by the rules — an opener CommonMark would not open (값**(합계)**를) matched with a mirror-shaped closer on the same line. Math with ASCII on both ends (x**(y)**z) is left out. A non-zero count marks an answer worth a look. next release

The other six are channel rewrites — what the channel needed changed before posting:

Field Counts
escaped char Characters the channel would read as syntax, escaped (GitHub’s \~ \< \*; Slack’s and Notion’s \*).
tag emphasis Emphasis the channel cannot read as markers, written another way — GitHub’s <strong>, or an invisible U+2060 inside Slack’s markers.
stripped HTML Source HTML removed: tags the channel cannot draw, comments, <br> turned into a line break.
rewritten bullet List markers rewritten (* • → - , Telegram’s - → • , 1) → 1.).
rewritten table Tables rewritten (separator and cell spacing, or turned into a monospace block).
converted marker Emphasis and links respelled — _italic_ → *italic*, __bold__ → **bold**, <url|text> → [text](url). Markdown channels only.

The streaming contract

A streamer takes the output token by token and returns what is safe to send now.

Two ways to send while streaming, depending on the channel:

The channel… Send Example
rewrites the whole message accumulated output plus preview() — what is still held, drawn as if the input ended here. After finish, skip the last edit when revised() is false. Telegram editMessageText, Slack chat.update, React
only appends each push piece as is. Never preview(). Slack appendStream

closeOpen() is the stricter sibling of preview(): it only closes block markup that is already out (<blockquote>, <pre>), without drawing what is held. Neither changes the streamer’s state.