mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
docs(telegram): move maintainer decisions into scoped AGENTS.md
This commit is contained in:
parent
6ad2626cfd
commit
3558391a75
3 changed files with 109 additions and 37 deletions
|
|
@ -1,37 +0,0 @@
|
|||
# Telegram Maintainer Decisions
|
||||
|
||||
Use this page during Telegram PR review. These are intentional maintainer decisions, not incidental implementation details.
|
||||
|
||||
Verified against Telegram Bot API 10.0, May 8 2026.
|
||||
|
||||
## Streaming
|
||||
|
||||
- Do not reintroduce `sendMessageDraft` for answer streaming. Telegram drafts are ephemeral 30-second previews in private chats; final delivery still requires a separate `sendMessage`. OpenClaw uses `sendMessage` plus `editMessageText`, then finalizes in place so the user sees one persistent answer.
|
||||
- Streaming owns one visible preview message. Edit it forward. Do not send an extra final bubble unless the final edit genuinely failed.
|
||||
- Keep the first-preview debounce. If a provider sends token-sized deltas, coalesce them into cumulative preview text instead of removing the debounce.
|
||||
- Respect Telegram limits in the Telegram layer. Text over 4096 chars chains into continuation messages. Polls keep the current Bot API 12-option cap.
|
||||
|
||||
## Telegram API Ownership
|
||||
|
||||
- Prefer grammY primitives and Telegram-native helpers when they model the behavior directly. Avoid custom Bot API wrappers for behavior grammY already owns.
|
||||
- Throttling is bot-token scoped. All Telegram API clients for the same token share one grammY `apiThrottler()` instance.
|
||||
- Do not silently retry failed topic sends without topic metadata. A wrong-surface success is worse than a loud Telegram error.
|
||||
- DM topics and forum topics are distinct. `direct_messages_topic_id` and `message_thread_id` are not interchangeable.
|
||||
|
||||
## Context And Authorization
|
||||
|
||||
- Reply context comes from OpenClaw-observed messages. Bot API updates expose `reply_to_message`, but there is no arbitrary `getMessage(chat, id)` hydration path later.
|
||||
- Current local chat context must outrank stale reply ancestry in the prompt. Old replied-to messages should not look like the active conversation.
|
||||
- Pairing is DM-only. Group and topic authorization need explicit config allowlists.
|
||||
- Telegram allowlists use numeric sender IDs. Usernames are optional, mutable, and not a reliable arbitrary-user lookup key in the Bot API.
|
||||
- Group and channel visible replies are policy-controlled. Normal room replies stay private unless `messages.groupChat.visibleReplies: "automatic"` is set or the agent explicitly calls `message.send`.
|
||||
|
||||
## Interactive Surfaces
|
||||
|
||||
- Native callbacks stay structured. Approval, native command, plugin, select, and multiselect callbacks must not fall through as raw callback text.
|
||||
- Preserve callback values exactly, including delimiters such as `env|prod`.
|
||||
- Native slash commands should remain fast-pathable before full workspace and agent-turn setup.
|
||||
|
||||
## Review Standard
|
||||
|
||||
Telegram behavior PRs need real Telegram proof when they touch transport, streaming, topics, callbacks, authorization, or reply context. Prefer the bot-to-bot QA lane or an equivalent live Telegram probe over synthetic-only validation.
|
||||
108
extensions/telegram/AGENTS.md
Normal file
108
extensions/telegram/AGENTS.md
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# Telegram Plugin Guide
|
||||
|
||||
Read this before any change under `extensions/telegram/`. These are intentional
|
||||
maintainer decisions and review-binding invariants, not incidental
|
||||
implementation details. Also read `extensions/AGENTS.md` for the plugin
|
||||
boundary rules.
|
||||
|
||||
Verified against Telegram Bot API 10.1, July 1 2026.
|
||||
|
||||
## Reliability Invariants
|
||||
|
||||
- Durable-before-ack on both transports. Polling: the ingress worker advances
|
||||
its offset only after the parent's committed spool enqueue. Webhook: respond
|
||||
200 only after the spool write; a spool-write failure returning non-200 is
|
||||
the redelivery contract, not an error to fix.
|
||||
- Completed spool rows tombstone via `complete()`, never `delete`. Telegram can
|
||||
refetch an update after dispatch, and callback side effects would rerun on a
|
||||
plain delete.
|
||||
- One retry policy. `spooled-update-retry-policy.ts` is the sole owner of spool
|
||||
backoff and dead-letter decisions; the polling and webhook drains both
|
||||
consume it. The dead-letter age gate is a product decision: over-limit
|
||||
updates keep retrying at the capped delay and only tombstone once older than
|
||||
the minimum age. Do not dead-letter on raw attempt counts, and do not
|
||||
"unstick" a lane by removing the gate.
|
||||
- Never swallow inbound processing errors. A transient store error on a
|
||||
spooled replay must record a `failed-retryable` processing result; a
|
||||
swallowed throw acks the update as completed and deletes the message.
|
||||
- No per-message full-store writes. Hot-path SQLite writes are per-entry.
|
||||
Rewriting a cache on every send or read stalls the event loop, and that
|
||||
stall masquerades as a polling stall (the sent-message-cache regression).
|
||||
- Transport error classification. The getUpdates worker retries Bot API 5xx
|
||||
and 429 locally, honoring `parameters.retry_after`; 401/404 stay fatal; 409
|
||||
must propagate to the parent session, which owns webhook-conflict recovery.
|
||||
Bot API errors carry `error_code`, not `.code`; parse non-2xx bodies
|
||||
defensively (a 502 HTML page is not JSON).
|
||||
- Send funnel parity. The durable funnel (`send.ts`) and the streaming funnel
|
||||
(`bot/delivery.*`) must degrade identically: rich-entity 400 falls back to
|
||||
plain text, caption parse 400 falls back to a plain caption, quote-not-found
|
||||
400 falls back to a legacy reply. New recoveries go into the shared
|
||||
predicates (`send-error-predicates.ts`, `reply-parameters.ts`), never into
|
||||
one funnel only.
|
||||
- Outbound flood waits honor `retry_after` up to
|
||||
`TELEGRAM_OUTBOUND_RETRY_AFTER_CAP_MS`; do not re-clamp Telegram sends to the
|
||||
generic channel retry ceiling.
|
||||
- Webhook security ordering. The secret header is validated first
|
||||
(constant-time compare, single-header enforcement, connection close on 401);
|
||||
the request rate limit budgets only failed-auth attempts so Telegram's own
|
||||
delivery is never throttled.
|
||||
- Every owned undici transport gets closed on all exit paths: polling session,
|
||||
webhook shutdown and startup failure, probe-cache eviction.
|
||||
|
||||
## Streaming
|
||||
|
||||
- Do not reintroduce `sendMessageDraft` for answer streaming. Telegram drafts
|
||||
are ephemeral 30-second previews in private chats; final delivery still
|
||||
requires a separate `sendMessage`. OpenClaw uses `sendMessage` plus
|
||||
`editMessageText`, then finalizes in place so the user sees one persistent
|
||||
answer.
|
||||
- Streaming owns one visible preview message. Edit it forward. Do not send an
|
||||
extra final bubble unless the final edit genuinely failed.
|
||||
- Keep the first-preview debounce. If a provider sends token-sized deltas,
|
||||
coalesce them into cumulative preview text instead of removing the debounce.
|
||||
- Respect Telegram limits in the Telegram layer. Text over 4096 chars chains
|
||||
into continuation messages. Polls keep the current Bot API 12-option cap.
|
||||
|
||||
## Telegram API Ownership
|
||||
|
||||
- Prefer grammY primitives and Telegram-native helpers when they model the
|
||||
behavior directly. Avoid custom Bot API wrappers for behavior grammY already
|
||||
owns.
|
||||
- Throttling is bot-token scoped. All Telegram API clients for the same token
|
||||
share one grammY `apiThrottler()` instance.
|
||||
- Do not silently retry failed topic sends without topic metadata. A
|
||||
wrong-surface success is worse than a loud Telegram error.
|
||||
- DM topics and forum topics are distinct. `direct_messages_topic_id` and
|
||||
`message_thread_id` are not interchangeable.
|
||||
|
||||
## Context And Authorization
|
||||
|
||||
- Reply context comes from OpenClaw-observed messages. Bot API updates expose
|
||||
`reply_to_message`, but there is no arbitrary `getMessage(chat, id)`
|
||||
hydration path later.
|
||||
- Current local chat context must outrank stale reply ancestry in the prompt.
|
||||
Old replied-to messages should not look like the active conversation.
|
||||
- Pairing is DM-only. Group and topic authorization need explicit config
|
||||
allowlists.
|
||||
- Telegram allowlists use numeric sender IDs. Usernames are optional, mutable,
|
||||
and not a reliable arbitrary-user lookup key in the Bot API.
|
||||
- Group and channel visible replies are policy-controlled. Normal room replies
|
||||
stay private unless `messages.groupChat.visibleReplies: "automatic"` is set
|
||||
or the agent explicitly calls `message.send`.
|
||||
|
||||
## Interactive Surfaces
|
||||
|
||||
- Native callbacks stay structured. Approval, native command, plugin, select,
|
||||
and multiselect callbacks must not fall through as raw callback text.
|
||||
- Preserve callback values exactly, including delimiters such as `env|prod`.
|
||||
- Native slash commands should remain fast-pathable before full workspace and
|
||||
agent-turn setup.
|
||||
|
||||
## Review Standard
|
||||
|
||||
- Telegram behavior PRs need real Telegram proof when they touch transport,
|
||||
streaming, topics, callbacks, authorization, or reply context. Prefer the
|
||||
bot-to-bot QA lane or an equivalent live Telegram probe over synthetic-only
|
||||
validation.
|
||||
- Reliability PRs (spool, drain, retry, ack, offset paths) need crash-window
|
||||
or restart-replay test proof, not just happy-path tests.
|
||||
1
extensions/telegram/CLAUDE.md
Symbolic link
1
extensions/telegram/CLAUDE.md
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
AGENTS.md
|
||||
Loading…
Add table
Add a link
Reference in a new issue