fix(matrix): split partial and quiet preview streaming (#61450)

Merged via squash.

Prepared head SHA: 6a0d7d1348
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
Gustavo Madeira Santana
2026-04-05 18:23:07 -04:00
committed by GitHub
parent 1582bbbfc5
commit 8a841b531f
16 changed files with 682 additions and 114 deletions

View File

@@ -178,9 +178,9 @@ This is a practical baseline config with DM pairing, room allowlist, and E2EE en
Matrix reply streaming is opt-in.
Set `channels.matrix.streaming` to `"partial"` when you want OpenClaw to send a single draft reply,
edit that draft in place while the model is generating text, and then finalize it when the reply is
done:
Set `channels.matrix.streaming` to `"partial"` when you want OpenClaw to send a single live preview
reply, edit that preview in place while the model is generating text, and then finalize it when the
reply is done:
```json5
{
@@ -193,15 +193,164 @@ done:
```
- `streaming: "off"` is the default. OpenClaw waits for the final reply and sends it once.
- `streaming: "partial"` creates one editable preview message for the current assistant block instead of sending multiple partial messages.
- `blockStreaming: true` enables separate Matrix progress messages. With `streaming: "partial"`, Matrix keeps the live draft for the current block and preserves completed blocks as separate messages.
- When `streaming: "partial"` and `blockStreaming` is off, Matrix only edits the live draft and sends the completed reply once that block or turn finishes.
- `streaming: "partial"` creates one editable preview message for the current assistant block using normal Matrix text messages. This preserves Matrix's legacy preview-first notification behavior, so stock clients may notify on the first streamed preview text instead of the finished block.
- `streaming: "quiet"` creates one editable quiet preview notice for the current assistant block. Use this only when you also configure recipient push rules for finalized preview edits.
- `blockStreaming: true` enables separate Matrix progress messages. With preview streaming enabled, Matrix keeps the live draft for the current block and preserves completed blocks as separate messages.
- When preview streaming is on and `blockStreaming` is off, Matrix edits the live draft in place and finalizes that same event when the block or turn finishes.
- If the preview no longer fits in one Matrix event, OpenClaw stops preview streaming and falls back to normal final delivery.
- Media replies still send attachments normally. If a stale preview can no longer be reused safely, OpenClaw redacts it before sending the final media reply.
- Preview edits cost extra Matrix API calls. Leave streaming off if you want the most conservative rate-limit behavior.
`blockStreaming` does not enable draft previews by itself.
Use `streaming: "partial"` for preview edits; then add `blockStreaming: true` only if you also want completed assistant blocks to remain visible as separate progress messages.
Use `streaming: "partial"` or `streaming: "quiet"` for preview edits; then add `blockStreaming: true` only if you also want completed assistant blocks to remain visible as separate progress messages.
If you need stock Matrix notifications without custom push rules, use `streaming: "partial"` for preview-first behavior or leave `streaming` off for final-only delivery. With `streaming: "off"`:
- `blockStreaming: true` sends each finished block as a normal notifying Matrix message.
- `blockStreaming: false` sends only the final completed reply as a normal notifying Matrix message.
### Self-hosted push rules for quiet finalized previews
If you run your own Matrix infrastructure and want quiet previews to notify only when a block or
final reply is done, set `streaming: "quiet"` and add a per-user push rule for finalized preview edits.
This is usually a recipient-user setup, not a homeserver-global config change:
Quick map before you start:
- recipient user = the person who should receive the notification
- bot user = the OpenClaw Matrix account that sends the reply
- use the recipient user's access token for the API calls below
- match `sender` in the push rule against the bot user's full MXID
1. Configure OpenClaw to use quiet previews:
```json5
{
channels: {
matrix: {
streaming: "quiet",
},
},
}
```
2. Make sure the recipient account already receives normal Matrix push notifications. Quiet preview
rules only work if that user already has working pushers/devices.
3. Get the recipient user's access token.
- Use the receiving user's token, not the bot's token.
- Reusing an existing client session token is usually easiest.
- If you need to mint a fresh token, you can log in through the standard Matrix Client-Server API:
```bash
curl -sS -X POST \
"https://matrix.example.org/_matrix/client/v3/login" \
-H "Content-Type: application/json" \
--data '{
"type": "m.login.password",
"identifier": {
"type": "m.id.user",
"user": "@alice:example.org"
},
"password": "REDACTED"
}'
```
4. Verify the recipient account already has pushers:
```bash
curl -sS \
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
"https://matrix.example.org/_matrix/client/v3/pushers"
```
If this returns no active pushers/devices, fix normal Matrix notifications first before adding the
OpenClaw rule below.
OpenClaw marks finalized text-only preview edits with:
```json
{
"com.openclaw.finalized_preview": true
}
```
5. Create an override push rule for each recipient account which should receive these notifications:
```bash
curl -sS -X PUT \
"https://matrix.example.org/_matrix/client/v3/pushrules/global/override/openclaw-finalized-preview" \
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"conditions": [
{ "kind": "event_match", "key": "type", "pattern": "m.room.message" },
{
"kind": "event_property_is",
"key": "content.m\\.relates_to.rel_type",
"value": "m.replace"
},
{
"kind": "event_property_is",
"key": "content.com\\.openclaw\\.finalized_preview",
"value": true
},
{ "kind": "event_match", "key": "sender", "pattern": "@bot:example.org" }
],
"actions": [
"notify",
{ "set_tweak": "sound", "value": "default" },
{ "set_tweak": "highlight", "value": false }
]
}'
```
Replace these values before you run the command:
- `https://matrix.example.org`: your homeserver base URL
- `$USER_ACCESS_TOKEN`: the receiving user's access token
- `@bot:example.org`: your OpenClaw Matrix bot MXID, not the receiving user's MXID
The rule is evaluated against the event sender:
- authenticate with the receiving user's token
- match `sender` against the OpenClaw bot MXID
6. Verify the rule exists:
```bash
curl -sS \
-H "Authorization: Bearer $USER_ACCESS_TOKEN" \
"https://matrix.example.org/_matrix/client/v3/pushrules/global/override/openclaw-finalized-preview"
```
7. Test a streamed reply. In quiet mode, the room should show a quiet draft preview and the final
in-place edit should notify once the block or turn finishes.
Notes:
- Create the rule with the receiving user's access token, not the bot's.
- New user-defined `override` rules are inserted ahead of default suppress rules, so no extra ordering parameter is needed.
- This only affects text-only preview edits that OpenClaw can safely finalize in place. Media fallbacks and stale-preview fallbacks still use normal Matrix delivery.
- If `GET /_matrix/client/v3/pushers` shows no pushers, the user does not yet have working Matrix push delivery for this account/device.
#### Synapse
For Synapse, the setup above is usually enough by itself:
- No special `homeserver.yaml` change is required for finalized OpenClaw preview notifications.
- If your Synapse deployment already sends normal Matrix push notifications, the user token + `pushrules` call above is the main setup step.
- If you run Synapse behind a reverse proxy or workers, make sure `/_matrix/client/.../pushrules/` reaches Synapse correctly.
- If you run Synapse workers, make sure pushers are healthy. Push delivery is handled by the main process or `synapse.app.pusher` / configured pusher workers.
#### Tuwunel
For Tuwunel, use the same setup flow and push-rule API call shown above:
- No Tuwunel-specific config is required for the finalized preview marker itself.
- If normal Matrix notifications already work for that user, the user token + `pushrules` call above is the main setup step.
- If notifications seem to disappear while the user is active on another device, check whether `suppress_push_when_active` is enabled. Tuwunel added this option in Tuwunel 1.4.2 on September 12, 2025, and it can intentionally suppress pushes to other devices while one device is active.
## Encryption and verification
@@ -833,7 +982,7 @@ Live directory lookup uses the logged-in Matrix account:
- `historyLimit`: max room messages to include as group history context. Falls back to `messages.groupChat.historyLimit`. Set `0` to disable.
- `replyToMode`: `off`, `first`, or `all`.
- `markdown`: optional Markdown rendering configuration for outbound Matrix text.
- `streaming`: `off` (default), `partial`, `true`, or `false`. `partial` and `true` enable single-message draft previews with edit-in-place updates.
- `streaming`: `off` (default), `partial`, `quiet`, `true`, or `false`. `partial` and `true` enable preview-first draft updates with normal Matrix text messages. `quiet` uses non-notifying preview notices for self-hosted push-rule setups.
- `blockStreaming`: `true` enables separate progress messages for completed assistant blocks while draft preview streaming is active.
- `threadReplies`: `off`, `inbound`, or `always`.
- `threadBindings`: per-channel overrides for thread-bound session routing and lifecycle.