feat: telegram draft streaming

This commit is contained in:
Peter Steinberger
2026-01-07 11:08:11 +01:00
parent e8420bd047
commit a700f9896d
26 changed files with 458 additions and 52 deletions

View File

@@ -468,6 +468,7 @@ Set `telegram.enabled: false` to disable automatic startup.
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["tg:123456789"], // optional; "open" requires ["*"]
groups: { "*": { requireMention: true } },
streamMode: "partial", // off | partial | block (draft streaming)
actions: { reactions: true }, // tool action gates (false disables)
mediaMaxMb: 5,
proxy: "socks5://localhost:9050",
@@ -478,6 +479,11 @@ Set `telegram.enabled: false` to disable automatic startup.
}
```
Draft streaming notes:
- Uses Telegram `sendMessageDraft` (draft bubble, not a real message).
- Requires **private chat topics** (message_thread_id in DMs; bot has topics enabled).
- `/reasoning stream` streams reasoning into the draft, then sends the final answer.
### `discord` (bot transport)
Configure the Discord bot by setting the bot token and optional gating:

View File

@@ -5,7 +5,7 @@ read_when:
---
# grammY Integration (Telegram Bot API)
Updated: 2025-12-07
Updated: 2026-01-07
# Why grammY
- TS-first Bot API client with built-in long-poll + webhook helpers, middleware, error handling, rate limiter.
@@ -19,6 +19,7 @@ Updated: 2025-12-07
- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `telegram.webhookUrl` is set (otherwise it long-polls).
- **Sessions:** direct chats collapse into the agent main session (`agent:<agentId>:<mainKey>`); groups use `agent:<agentId>:telegram:group:<chatId>`; replies route back to the same provider.
- **Config knobs:** `telegram.botToken`, `telegram.dmPolicy`, `telegram.groups` (allowlist + mention defaults), `telegram.allowFrom`, `telegram.groupAllowFrom`, `telegram.groupPolicy`, `telegram.mediaMaxMb`, `telegram.proxy`, `telegram.webhookSecret`, `telegram.webhookUrl`.
- **Draft streaming:** optional `telegram.streamMode` uses `sendMessageDraft` in private topic chats (Bot API 9.3+).
- **Tests:** grammy mocks cover DM + group mention gating and outbound send; more media/webhook fixtures still welcome.
Open questions

View File

@@ -5,7 +5,7 @@ read_when:
---
# Telegram (Bot API)
Updated: 2026-01-06
Updated: 2026-01-07
Status: production-ready for bot DMs + groups via grammY. Long-polling by default; webhook optional.
@@ -44,6 +44,10 @@ Telegram forum topics include a `message_thread_id` per message. Clawdbot:
- Sends typing indicators and replies with `message_thread_id` so responses stay in the topic.
- Exposes `MessageThreadId` + `IsForum` in template context for routing/templating.
Private topics (DM forum mode) also include `message_thread_id`. Clawdbot:
- Appends `:topic:<threadId>` to **DM** session keys for isolation.
- Uses the thread id for draft streaming + replies.
## Access control (DMs + groups)
- Default: `telegram.dmPolicy = "pairing"`. Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
- Approve via:
@@ -69,6 +73,27 @@ Telegram supports optional threaded replies via tags:
Controlled by `telegram.replyToMode`:
- `off` (default), `first`, `all`.
## Streaming (drafts)
Telegram can stream **draft bubbles** while the agent is generating a response.
Clawdbot uses Bot API `sendMessageDraft` (not real messages) and then sends the
final reply as a normal message.
Requirements (Telegram Bot API 9.3+):
- **Private chats with topics enabled** (forum topic mode for the bot).
- Incoming messages must include `message_thread_id` (private topic thread).
- Streaming is ignored for groups/supergroups/channels.
Config:
- `telegram.streamMode: "off" | "partial" | "block"` (default: `partial`)
- `partial`: update the draft bubble with the latest streaming text.
- `block`: update the draft bubble in larger blocks (chunked).
- `off`: disable draft streaming.
Reasoning stream (Telegram only):
- `/reasoning stream` streams reasoning into the draft bubble while the reply is
generating, then sends the final answer without reasoning.
- If `telegram.streamMode` is `off`, reasoning stream is disabled.
## Agent tool (reactions)
- Tool: `telegram` with `react` action (`chatId`, `messageId`, `emoji`).
- Reaction removal semantics: see [/tools/reactions](/tools/reactions).
@@ -92,6 +117,7 @@ Provider options:
- `telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
- `telegram.replyToMode`: `off | first | all`.
- `telegram.textChunkLimit`: outbound chunk size (chars).
- `telegram.streamMode`: `off | partial | block` (draft streaming).
- `telegram.mediaMaxMb`: inbound/outbound media cap (MB).
- `telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).
- `telegram.webhookUrl`: enable webhook mode.

View File

@@ -603,7 +603,7 @@ Quick reference (send these in chat):
| `/activation mention\|always` | Group activation (owner-only) |
| `/think <level>` | Set thinking level (off\|minimal\|low\|medium\|high) |
| `/verbose on\|off` | Toggle verbose mode |
| `/reasoning on\|off` | Toggle reasoning visibility |
| `/reasoning on\|off\|stream` | Toggle reasoning visibility (stream = Telegram draft only) |
| `/elevated on\|off` | Toggle elevated bash mode (approved senders only) |
| `/model <name>` | Switch AI model (see below) |
| `/queue <mode>` | Queue mode (see below) |

View File

@@ -40,7 +40,7 @@ Text + native (when enabled):
- `/reset` or `/new`
- `/think <level>` (aliases: `/thinking`, `/t`)
- `/verbose on|off` (alias: `/v`)
- `/reasoning on|off` (alias: `/reason`)
- `/reasoning on|off|stream` (alias: `/reason`; `stream` = Telegram draft only)
- `/elevated on|off` (alias: `/elev`)
- `/model <name>`
- `/queue <mode>` (plus options like `debounce:2s cap:25 drop:summarize`)

View File

@@ -35,9 +35,10 @@ read_when:
- When verbose is on, agents that emit structured tool results (Pi, other JSON agents) send each tool result back as its own metadata-only message, prefixed with `<emoji> <tool-name>: <arg>` when available (path/command); the tool output itself is not forwarded. These tool summaries are sent as soon as each tool finishes (separate bubbles), not as streaming deltas. If you toggle `/verbose on|off` while a run is in-flight, subsequent tool bubbles honor the new setting.
## Reasoning visibility (/reasoning)
- Levels: `on|off`.
- Levels: `on|off|stream`.
- Directive-only message toggles whether thinking blocks are shown as italic text in replies.
- When enabled, any model-provided reasoning content is appended as a separate italic block.
- `stream` (Telegram only): streams reasoning into the Telegram draft bubble while the reply is generating, then sends the final answer without reasoning.
- Alias: `/reason`.
## Related

View File

@@ -6,7 +6,7 @@ read_when:
---
# TUI (Gateway chat client)
Updated: 2026-01-03
Updated: 2026-01-07
## What it is
- A terminal UI that connects to the Gateway WebSocket and speaks the same chat APIs as WebChat.
@@ -51,7 +51,7 @@ Use SSH tunneling or Tailscale to reach the Gateway WS.
- `/model <provider/model>` (or `/model list`, `/models`)
- `/think <off|minimal|low|medium|high>`
- `/verbose <on|off>`
- `/reasoning <on|off>`
- `/reasoning <on|off|stream>` (stream = Telegram draft only)
- `/elevated <on|off>`
- `/elev <on|off>`
- `/activation <mention|always>`