Live mock, real pipeline. Paste a real URL into the composer below.
Before send — the server fetches the page and a preview chip appears above the input (✕ to dismiss).
After send — the message carries the frozen card, and the right panel shows the model context as an
append-only timeline: the T1 skeleton digest goes in at send; click an outline pill to play a persona
ordering a full section via the in-band <read> tag (T2, appended next turn).
Backend not reachable — start it with scripts\serve-unfurl-8013.bat
then open http://127.0.0.1:8013/.
the room — composer + bubble
room · mock
your messages appear here
try:
what the persona sees — real extraction
no link sent yet
After you hit send, the model context grows here as an
append-only timeline —
[L1 · T0+T1 digest] injected at send: title/source header,
outline, lede, first paragraph per
section, conclusion (≤ 6,000 chars)
[L1 · T2 「section」] appended ONLY when a persona orders it
by emitting <read link="L1" section="…">
Click an outline pill inside a digest block to play the
persona and order a section.
architectureThe server fetches, the message freezes.
Browsers can't read foreign pages (CORS), so the box does the fetch — Telegram's model. But the result is
stored into the message at send time and never re-fetched — WhatsApp's persistence. That combination is
what keeps the §8 append-only prompt cache valid.
tiersDepth follows engagement, not position.T0 header (~150 chars) + T1 skeleton digest (outline · lede · first paragraph per section ·
conclusion, ≤ 6,000 chars) injected at send — uniform shallow coverage of the whole article instead of a
hard head-cut. T2 full sections appended only when ordered, so links nobody engages with stay cheap.
Pages without heading structure fall back to the 12,000-char paragraph-boundary cap. At §9 compaction a
link collapses back to T0 + a one-line gist.
read tagHow a persona orders T2. It emits
<read link="L3" section="…"> in its output — the same in-band trick as
<speak>/<confer>; our code validates the id + heading against the
frozen extract and appends the section next turn (no second model call; a one-beat delay is natural
in a chat room). The T1 outline is the menu it can order from. Guardrails: max 2 fulfilments per turn,
dedup, ids anchored — never re-matched from names.
guardhttp/https only · private IPs refused on every redirect hop ·
10 s timeout · 2.5 MB read cap. The box fetches user-supplied URLs, so the SSRF guard is part of the
pipeline from day one, not an afterthought.
dismiss✕ means nothing is attached. No card for humans,
no context for personas — the URL goes out as plain linkified text. Same URL won't re-unfurl until it changes.
link unfurl mock · backend: docs/unfurl_server.py (port 8013) ·
production target: lib/run_room.py