#16102: Fix: Telegram Inline Button Support for Exec Approvals (builds on #9265)
channel: telegram
stale
size: L
Cluster:
Telegram Inline Button Enhancements
## Summary
Fixes and extends the Telegram inline button support for exec approvals originally proposed in #9265 by @vishaltandale00. Resolves issues #9229 and #15504.
This PR addresses several bugs in the original implementation that prevented it from passing CI and working correctly in production.
## What Changed
### 1. Gateway URL Resolution (`src/telegram/bot.ts`)
- **Problem:** Original code used `cfg.gateway?.url` which doesn't exist on `GatewayConfig`. The fallback `ws://127.0.0.1:18789` breaks for non-default port/bind configurations.
- **Fix:** Uses `buildGatewayConnectionDetails({ config: cfg }).url` to correctly resolve the gateway URL in both local and remote modes.
### 2. Type Safety (`src/telegram/bot-native-commands.ts`)
- **Problem:** `resolveApproval` was typed as `(approvalId: string, decision: string)`, too broad for the actual `ExecApprovalDecision` union type. `ExecApprovalDecision` was imported from `./exec-approvals.js` where it isn't exported.
- **Fix:** Import `ExecApprovalDecision` from `../infra/exec-approvals.js` (where it's exported) and use it in the interface definition.
### 3. Multiple Approver Support (`src/telegram/exec-approvals.ts`)
- **Problem:** When multiple approvers were configured, only the last approver's message was tracked/updated on resolve/timeout.
- **Fix:** Store an array of `{ telegramChatId, telegramMessageId }` per request, with a single timeout. On resolve/timeout, all approver messages are updated.
### 4. Callback Data Length (`src/telegram/exec-approvals.ts`)
- **Problem:** Telegram limits `callback_data` to 64 bytes. The original format (`execapproval:id={uuid};action=allow-once`) exceeded this at ~71 bytes for UUIDs.
- **Fix:** Shortened format: prefix `ea` instead of `execapproval`, actions `o`/`a`/`d` instead of full strings. New format: `ea:id={id};a={o|a|d}` (~43 bytes for UUID).
### 5. Plain-Text De-duplication (`src/infra/exec-approval-forwarder.ts`)
- **Problem:** Two separate mechanisms sent approval notifications to Telegram: the plain-text forwarder AND the new button handler, causing duplicate messages.
- **Fix:** Added `shouldSkipPlainTextForTarget()` — when a channel has button-based exec approvals enabled, the forwarder skips sending plain-text notifications to that target.
### 6. Zod Schema Validation (`src/config/zod-schema.providers-core.ts`)
- **Problem:** The `execApprovals` config block was added to TypeScript types but not to the Zod validation schema, causing gateway startup rejection ("Unrecognized key").
- **Fix:** Added the `execApprovals` object schema to `TelegramAccountSchemaBase`.
## Testing
- ✅ `pnpm build` — passes
- ✅ `pnpm check` — passes
- ✅ `pnpm test` — passes
- ✅ Tested with `execApprovals.enabled: true` + `inlineButtons: "all"` — single message with buttons, one-tap approve works
- ✅ Tested with `execApprovals.enabled: true` + `inlineButtons: "off"` — falls back to plain-text `/approve` commands
- ✅ Tested re-enabling `inlineButtons` after fallback — buttons return correctly
- ✅ No duplicate messages after de-duplication fix
## AI Disclosure 🤖
This PR was developed collaboratively using:
- **Claude Opus 4** (via OpenClaw agent "LobsterBot") for code analysis, type fixes, and debugging
- **Cursor** IDE agent for the de-duplication fix, callback data optimization, and multiple approver support
- **Degree of testing:** Fully tested on a live OpenClaw deployment with Telegram integration
- We understand what the code does and have manually verified all changes.
Builds on the original work by @vishaltandale00 in #9265.
Related issues: #9229, #15504, #6397
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes Telegram inline button support for exec approvals, addressing several bugs from the original implementation in #9265. The changes include proper gateway URL resolution, type safety improvements, multiple approver support, callback data optimization, plain-text deduplication, and Zod schema validation.
**Key improvements:**
- Gateway URL now correctly resolved using `buildGatewayConnectionDetails()` instead of non-existent `cfg.gateway?.url`
- Type safety fixed by importing `ExecApprovalDecision` from the correct module
- Multiple approvers now properly tracked with array of message IDs
- Callback data compressed to ~46 bytes (well under Telegram's 64-byte limit)
- Plain-text notifications skipped when button UI is enabled to prevent duplicates
- Zod schema updated to accept `execApprovals` config
**Issue found:**
- Memory leak in `src/telegram/exec-approvals.ts:331-406` when all message sends fail - request is added to `requestCache` before sends are attempted, but only added to `pending` if at least one send succeeds, leaving orphaned entries in `requestCache` that are never cleaned up
<h3>Confidence Score: 4/5</h3>
- This PR is mostly safe to merge with one moderate memory leak issue that should be addressed
- The implementation is solid overall - it fixes real bugs from the original PR, has proper type safety, handles edge cases well (duplicate chat IDs, invalid approver IDs, message send failures), and includes comprehensive testing. However, there's a memory leak bug where failed message sends leave orphaned entries in requestCache. This won't cause immediate problems but could accumulate over time in production.
- Pay close attention to `src/telegram/exec-approvals.ts` - the memory leak on line 331 should be fixed before merge
<sub>Last reviewed commit: 8c93258</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#22095: feat: add Telegram inline buttons to exec approval requests
by AIflow-Labs · 2026-02-20
86.7%
#9265: Feature: Telegram Inline Button Support for Exec Approvals
by vishaltandale00 · 2026-02-05
86.3%
#19213: Telegram: preserve DM topic thread in direct replies
by Kemalau · 2026-02-17
79.5%
#19399: telegram: fix MEDIA false positives and partial final drop
by HOYALIM · 2026-02-17
79.2%
#22434: feat(telegram): support sending original quality images
by godenjan · 2026-02-21
77.2%
#19050: fix(telegram): skip message_thread_id for private chats to prevent ...
by Limitless2023 · 2026-02-17
76.9%
#22469: fix(gateway): avoid stale whatsapp labels on direct sessions
by loganprit · 2026-02-21
76.8%
#19829: fix(telegram): fall back to default scope for array capabilities wi...
by NewdlDewdl · 2026-02-18
76.7%
#19673: fix(telegram): avoid starting streaming replies with only 1-2 words
by emanuelst · 2026-02-18
76.6%
#8166: fix(telegram): lifecycle fixes for duplicate messages and auto-reco...
by cheenu1092-oss · 2026-02-03
76.5%