diff --git a/docs/code/agents.md b/docs/code/agents.md
index 0a10d6a..8286b6f 100644
--- a/docs/code/agents.md
+++ b/docs/code/agents.md
@@ -114,6 +114,21 @@ configuration, and the bin in the panel's header starts it over. `MCP_ENABLED`
is not needed for it: the endpoint is for agents outside, the chat for the
one inside.
+## Control
+
+Where a live flow has a [`wren` node](../reference/node-types.md#wren) with
+its `prompt` and `actions` wired, the chat gains a **Control** tab, and Home a
+chat button at the top right of the brain graph (in the header, on a phone)
+that opens it. No provider is needed for this one: the model is the node.
+
+A command is published to the node's `prompt` message as a value from the
+chat, and the answer is the next value of its `actions`: each call, the
+message it went to, whether it was applied, and how sure the model was. A
+failing node answers with its error, and one that says nothing for fifteen
+seconds is reported as such. With several nodes, a picker in the header
+chooses. The conversation is kept in the browser only, and a reload clears
+it.
+
## A note on working this way
The pattern that works is the same one that works with a person: let the agent
diff --git a/docs/interface/index.md b/docs/interface/index.md
index bc5e618..5c49d9c 100644
--- a/docs/interface/index.md
+++ b/docs/interface/index.md
@@ -53,6 +53,10 @@ its label showing so you can see which one it is without hovering.
Each flow also has a switch beside it in the list, which starts and stops it.
+A [`wren` node](../reference/node-types.md#wren) anywhere adds a chat button
+at the graph's top right, in the header on a phone, which opens the chat on
+its [Control tab](../code/agents.md#control).
+
### Flows and dashboards
Two columns under the graph, exactly as tall as each other, most recently
diff --git a/docs/reference/node-types.md b/docs/reference/node-types.md
index 1d515d8..19a1f74 100644
--- a/docs/reference/node-types.md
+++ b/docs/reference/node-types.md
@@ -290,6 +290,87 @@ reloads identically.
A worked example of numeric logic, not a modelling tool. To train something,
use a [batch flow](../concepts/runs.md) and a Function node.
+## Language
+
+### Wren
+
+**`wren`** — turn a typed command into values for the devices wired to it.
+
+[Wren](https://github.com/stroblme/wren) is a small language model that runs
+on the engine's CPU: about 40 MB, a fifth of a second per command. It reads the
+house as text, the areas, the devices in each and their states, and answers a
+command with calls on those devices. It is a separate package, installed into
+the engine with `pip install wren`; without it the node says so on the canvas.
+
+| Input | Type | Notes |
+|---|---|---|
+| `prompt` | `str` | the command, and the only input that runs the node |
+| `presence` | `str` | the area the user is in; anything else reads as away |
+| `outdoor` | `float` | °C; 20 without it |
+| `dark` | `bool` | without it, dark before 07:00 and from 18:00 |
+
+The outputs are `actions`, a list of records, and any number of **targets**. A
+target is an output named `_` and bound to the message its device
+listens on: `living_room_setpoint` bound to `home.setpoint` is the living
+room's heating. The wiring is the allowlist, so the model can write nothing
+that is not wired to the node.
+
+- **area**: `living_room`, `kitchen`, `bathroom`, `bedroom`, `office`,
+ `garden`, `patio`, `garage`, `driveway`, `balcony`
+- **kind**: `light`, `lamp`, `brightness` for a light; `setpoint`, `heating`,
+ `heat`, `boost` for heating; `media`, `music`, `tv`; `shutter`, `blind`,
+ `cover`; `water`, `watering`, `sprinkler`
+
+A port naming anything else is refused when the flow is built. The editor
+names a port after its message, so there a target's message carries the name;
+binding a port to a message named otherwise, as `{"name": "setpoint", "port":
+"living_room_setpoint"}`, takes the flow API.
+
+Targets of one area and kind are one device to the model, and each takes what
+a call means for its type. A target's current value is the state the model
+reads.
+
+| Call | `bool` | number | `str` |
+|---|---|---|---|
+| `lights.on` / `off` | `true` / `false` | `100` / `0` | `on` / `off` |
+| `media.play` / `stop` | `true` / `false` | — | `play` / `stop` |
+| `heating.set` | — | the temperature | — |
+| `heating.boost` / `off` | `true` / `false` | — | `boost` / `off` |
+| `shutters.up` / `down` | `true` / `false` | `100` / `0` | `up` / `down` |
+| `shutters.stop` | — | — | `stop` |
+| `watering.start` / `stop` | `true` / `false` | — | `on` / `off` |
+
+| Setting | Default | Notes |
+|---|---|---|
+| `apply` | `false` | write to the targets; off, the node only reports |
+| `min_confidence` | `0.93` | below this nothing is written |
+
+Each record in `actions` is `{message, value, applied, delay_s, call,
+confidence}`, one per call and target. A call nothing is wired for, such as
+`sensors.temperature` or a light in a room without one, gets a record with an
+empty `message`. The confidence is the lowest probability the model gave any
+part of its answer: at the default threshold, nine in ten right answers pass,
+and 38 of the 40 wrong ones in the model's own evaluation do not.
+
+A call with a delay ("in 10m") is held by the engine's work queue, like the
+Trigger node's second value: it survives a restart where the queue is in
+Redis, and not otherwise.
+
+Every house the model was trained on had a living room with a light, media
+and heating, and a light in every other area. The node fills in what is not
+wired, so the model reads a house it knows; a call on one of those is
+reported, never applied.
+
+!!! note "What the current model cannot do"
+
+ No levels: a light is on or off, never dimmed, coloured or half open.
+ Delays are whole minutes. Only one device per kind and area. Only the ten
+ areas above: one it was not trained on is answered confidently and wrongly,
+ which is why such a port is refused rather than tried. Commands are English,
+ phrased roughly like "turn on the lights in the kitchen".
+
+The chat's [Control tab](../code/agents.md#control) sends commands by hand.
+
## Connectors
Anything else in the palette came from an installed **connector** package: a