Specification · Section 0 of 14
Sixty seconds
The whole idea in sixty seconds: a fenced block going down, a structured tag coming up, and nothing in between.
ANVIL has exactly two halves, and nothing in between.
┌──────────────────────────┐ ┌──────────────────────────┐
│ DOWN: a fenced block │ │ UP: a structured tag │
│ the agent writes │ ─── human ──► │ the client writes text. │
│ markdown. The host │ clicks │ The agent reads text. │
│ renders it as REAL UI. │ │ │
└──────────────────────────┘ └──────────────────────────┘
```anvil <stamp …>
Down, mid-sentence, in an ordinary assistant message:
Nice. Which of these feels like you?
```anvil
@gallery id=mood select=many max=2
? Pick the ones that feel right
- ed | Editorial | dense, warm | img=https://example.com/ed.jpg
- sw | Swiss | airy, cold | img=https://example.com/sw.jpg
- br | Brutal | loud, black | img=https://example.com/br.jpg
```
That renders as a row of clickable cards. The human picks two and confirms. The cards stamp: the picked ones lift, the rest fade, permanently.
Up, into the model’s context, as a plain user turn:
<stamp block="mood" kind="gallery" values="ed,br" labels="Editorial,Brutal">
I picked Editorial and Brutal.
</stamp>
That tag is the entire integration. No tool calls, no widget runtime, no event bus reaching into the agent loop. The model emits markdown and reads text; everything between is the host’s problem.
The attributes are the truth. The sentence inside is the courtesy — it exists so the transcript still reads like a conversation months later, and so a model that ignores the tag entirely still gets the gist.