Telegram forum topic names now in agent context
Telegram forum bots can now reference topics by human-readable names like "Design-learning" instead of opaque numeric IDs. Topic names are learned from Telegram service messages and cached in a bounded LRU.
Telegram's Bot API exposes numeric topic IDs but withholds topic names — leaving agents to work with cryptic numbers like "563" instead of usable labels. The fix intercepts Telegram's forum service messages (topic created, edited, closed, reopened) to build a bounded in-memory cache mapping thread IDs to human-readable names. When a message arrives in a forum topic, the cache is consulted and the name is injected into the agent's prompt metadata alongside the existing topic_id. The module uses a true LRU strategy via updatedAt timestamps rather than insertion order, capped at 2048 entries to prevent unbounded growth. A fallback seed from reply_to_message.forum_topic_created handles topics created before the bot started listening. The feature plugs into the existing type so templates gain {{TopicName}} substitution, and plugin hooks receive topicName in their event metadata — enabling downstream consumers like topic-specific config loaders to operate on names rather than IDs. This addresses a long-standing gap flagged across several linked issues around agent routing and topic-specific context in Telegram forums.
View Original GitHub Description
Summary
- Problem: Telegram Bot API doesn't expose a method to look up forum topic names by
message_thread_id. Agents only see a numerictopic_id(e.g.563) with no way to know the human-readable name. - Why it matters: Agents need topic names to provide contextual responses, auto-match topic-specific config/files, and display meaningful labels — all impossible with just a numeric ID.
- What changed:
- New bounded in-memory LRU cache (
topic-name-cache.ts) populated fromforum_topic_created,forum_topic_edited,forum_topic_closed,forum_topic_reopenedservice messages - Fallback seed from
reply_to_message.forum_topic_createdfor topics created before the bot started TopicNameadded toMsgContext(available as{{TopicName}}in templates)topic_nameadded to the agent prompt metadata block (alongside existingtopic_id)topicNamepropagated to plugin hook event metadata
- New bounded in-memory LRU cache (
- What did NOT change: Existing
MessageThreadId,IsForum,topic_idfields. Non-forum messages. Other channels. No new dependencies.
Change Type (select all)
- Bug fix
- Feature
- Refactor required for the fix
- Docs
- Security hardening
- Chore/infra
Scope (select all touched areas)
- Gateway / orchestration
- Skills / tool execution
- Auth / tokens
- Memory / storage
- Integrations
- API / contracts
- UI / DX
- CI/CD / infra
Linked Issue/PR
- Closes #36260
- Related #43007, #43231, #33398
- Related #36916 (prior attempt, closed — this PR addresses all review feedback from that PR)
Root Cause (if applicable)
N/A — new feature, not a bug fix.
Regression Test Plan (if applicable)
- Coverage level that should have caught this:
- Unit test
- Seam / integration test
- End-to-end test
- Existing coverage already sufficient
- Target test or file:
extensions/telegram/src/topic-name-cache.test.ts - Scenario the test should lock in: Cache stores/retrieves topic names, handles renames, tracks close/reopen, respects LRU eviction at 2048 entries
- Why this is the smallest reliable guardrail: The cache is a pure function module with no I/O; unit tests cover all branches including eviction
- If no new test is added, why not: New tests ARE added (9 test cases)
User-visible / Behavior Changes
topic_namenow appears in the agent's "Conversation info (untrusted metadata)" block for Telegram forum topic messages. Example:topic_name: "Design-learning"alongsidetopic_id: "42".{{TopicName}}is available in prompt templates.- Plugin hooks (
message:received, claim events, internal contexts) now includetopicNamein metadata. - Cache is in-memory only; after a gateway restart, falls back to the creation-time name from
reply_to_message.forum_topic_createduntil a live rename event repopulates.
Diagram (if applicable)
Before:
[telegram msg in topic] -> agent sees: topic_id: "563"
After:
[telegram msg in topic] -> topic-name-cache lookup -> agent sees: topic_id: "563", topic_name: "Design-learning"
Cache population:
[forum_topic_created msg] -> updateTopicName(chatId, threadId, {name})
[forum_topic_edited msg] -> updateTopicName(chatId, threadId, {name}) (captures renames)
[forum_topic_closed msg] -> updateTopicName(chatId, threadId, {closed: true})
[regular msg with reply_to_message.forum_topic_created] -> seed cache if empty (creation-time name)
Security Impact (required)
- New permissions/capabilities?
No - Secrets/tokens handling changed?
No - New/changed network calls?
No - Command/tool execution surface changed?
No - Data access scope changed?
No
Repro + Verification
Environment
- OS: macOS (dev) + Amazon Linux 2023 on EC2 (VPS test)
- Runtime: Node.js 22
- Integration/channel: Telegram (forum supergroup)
- Relevant config: Standard OpenClaw Telegram config with forum supergroup enabled
Steps
- Deploy OpenClaw with this change to a VPS connected to a Telegram bot
- Create or use an existing forum topic in a supergroup where the bot is a member
- Send a message in the topic
- Observe the agent's response — it should reference the topic name
- Rename the topic, send another message — agent should reflect the new name
Expected
topic_namepopulated in agent prompt metadata with the current topic name- Renames captured live from
forum_topic_editedservice messages
Actual
- Verified working end-to-end on live Telegram forum supergroup
Evidence
- Failing test/log before + passing after
- Trace/log snippets
- Screenshot/recording
- Perf numbers (if relevant)
Test results:
pnpm test:extension telegram
Test Files 91 passed (91)
Tests 1224 passed (1224)
pnpm test:contracts
exit_code: 0
Debug trace confirming rename capture:
[TOPIC-DEBUG] chat=-1003633263090 thread=563 ftEdited={"name":"GLM vs Gemma vs M2.7"}
[TOPIC-DEBUG] stored from ftEdited: "GLM vs Gemma vs M2.7"
[TOPIC-DEBUG] final resolved name for -1003633263090:563 = "GLM vs Gemma vs M2.7"
Human Verification (required)
- Verified scenarios: forum_topic_created seeding from reply_to_message, forum_topic_edited live rename capture, topic_name appearing in agent prompt metadata, agent referencing topic name in natural language response
- Edge cases checked: cold cache (restart then fallback to creation-time name), LRU eviction at 2048 entries (unit test), missing message_thread_id, non-forum chats
- What I did not verify: forum_topic_closed / forum_topic_reopened live (unit tested only), other channels unaffected (code inspection only)
Review Conversations
- I replied to or resolved every bot review conversation I addressed in this PR.
- I left unresolved only the conversations that still need reviewer or maintainer judgment.
Compatibility / Migration
- Backward compatible?
Yes — new optional field, existing behavior unchanged - Config/env changes?
No - Migration needed?
No
Risks and Mitigations
- Risk: Cache is in-memory only; topic names lost on restart
- Mitigation: Fallback seed from
reply_to_message.forum_topic_createdprovides creation-time name immediately. Live renames repopulate the cache. Persistence could be added as a follow-up.
- Mitigation: Fallback seed from
- Risk: Eviction scan is O(n) over cache entries
- Mitigation: Cache is capped at 2048 entries; linear scan over 2048 entries is sub-millisecond. Only runs when cache is full and a new entry is inserted.
AI-Assisted PR Disclosure
- This PR was AI-assisted (Cursor + Claude)
- Testing level: fully tested — unit tests + live Telegram verification on VPS
- I understand what the code does
- Bot review conversations will be resolved as addressed
Addresses all feedback from the prior closed PR #36916:
FIFO eviction→ true LRU viaupdatedAttimestamp (not Map insertion order)Unsafe→ uses grammy's native typed fields directlyRecord<string, unknown>casttopic_name missing from prompt metadata (P1)→ added toinbound-meta.ts+ plugin hooks- Added
reply_to_message.forum_topic_createdseed (not in #36916) - Added
forum_topic_closed/forum_topic_reopenedlifecycle handling (not in #36916)