Skip to content

Telegram: fix named-account DM topic session keys#48773

Merged
vincentkoc merged 1 commit intoopenclaw:mainfrom
vincentkoc:vincentkoc-code/fix-48204-telegram-session-status
Mar 17, 2026
Merged

Telegram: fix named-account DM topic session keys#48773
vincentkoc merged 1 commit intoopenclaw:mainfrom
vincentkoc:vincentkoc-code/fix-48204-telegram-session-status

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

Summary

  • Problem: Telegram named-account DM topics could route normal messages to the per-account session while native /status still targeted agent:main:main:thread:..., creating a phantom empty session.
  • Why it matters: /status would show 0 usage/context for a live thread even when the real session had token data.
  • What changed: extracted a shared Telegram base-session-key resolver and used it in inbound message context building, native command routing, and Telegram session-state lookup.
  • What did NOT change (scope boundary): no session-store migration, no cross-channel routing changes, no changes to default-account Telegram DM behavior.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • 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

User-visible / Behavior Changes

  • Telegram named-account DM topics now resolve /status and related session lookups against the same canonical session key as inbound message handling.
  • This avoids phantom agent:main:main:thread:... sessions winning over the real per-account Telegram session.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22 / Vitest channel config
  • Model/provider: N/A
  • Integration/channel (if any): Telegram
  • Relevant config (redacted): non-default Telegram account DM fallback with DM topics

Steps

  1. Configure a non-default Telegram account without an explicit DM binding.
  2. Use a Telegram DM topic so inbound traffic lands on a thread session.
  3. Run /status in that topic.

Expected

  • /status resolves the same canonical per-account Telegram thread session used by normal inbound message handling.

Actual

  • Before this change, native command routing could target agent:main:main:thread:... while normal traffic used agent:main:telegram:<account>:direct:...:thread:....

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: named-account DM fallback key selection, DM topic thread suffixing, existing named-account DM message-context coverage.
  • Edge cases checked: default account still keeps agent:main:main; named-account group traffic still does not use the DM fallback path.
  • What you did not verify: full GitHub CI matrix and live Telegram roundtrip against a real bot.

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.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert this PR or restore the previous route.sessionKey call sites in Telegram routing.
  • Files/config to restore: extensions/telegram/src/conversation-route.ts, extensions/telegram/src/bot-message-context.ts, extensions/telegram/src/bot-native-commands.ts, extensions/telegram/src/bot-handlers.ts
  • Known bad symptoms reviewers should watch for: Telegram named-account DM topics resolving to the wrong session key again, or default-account DMs unexpectedly leaving agent:main:main.

Risks and Mitigations

  • Risk: another Telegram code path may still read route.sessionKey directly when it should use the normalized named-account DM base key.
    • Mitigation: centralize the fallback logic in resolveTelegramConversationBaseSessionKey and add targeted regression coverage for the helper and message-context path.

@openclaw-barnacle openclaw-barnacle bot added the channel: telegram Channel integration: telegram label Mar 17, 2026
@vincentkoc vincentkoc self-assigned this Mar 17, 2026
@openclaw-barnacle openclaw-barnacle bot added size: S maintainer Maintainer-authored PR labels Mar 17, 2026
@Ryce

This comment was marked as spam.

@vincentkoc vincentkoc marked this pull request as ready for review March 17, 2026 07:41
@vincentkoc vincentkoc merged commit 1eb810a into openclaw:main Mar 17, 2026
26 of 39 checks passed
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 17, 2026

Greptile Summary

This PR fixes a session-key mismatch for Telegram named-account DM topics by extracting a shared resolveTelegramConversationBaseSessionKey helper in conversation-route.ts and using it consistently across bot-message-context.ts, bot-native-commands.ts (/status), and the resolveTelegramSessionState lookup in bot-handlers.ts. Previously, native commands like /status fell back to the raw route.sessionKey (agent:main:main), creating a phantom empty session while actual inbound traffic ran on agent:main:telegram:<account>:direct:<chatId>.

Key changes:

  • New resolveTelegramConversationBaseSessionKey helper centralizes the named-account DM fallback logic (accountId !== DEFAULT_ACCOUNT_ID && matchedBy === "default" + not a group → build per-account dmScope: "per-account-channel-peer" key).
  • bot-message-context.ts drops the 22-line inline equivalent and uses the new helper; unused imports (buildAgentSessionKey, resolveTelegramDirectPeerId) are cleaned up.
  • /status command path and session-state lookup are aligned with the inbound message session key.
  • Test mock in bot-message-context.named-account-dm.test.ts is improved: the old mock replaced the entire "../../../src/channels/session.js" module; the new one spreads actual from "openclaw/plugin-sdk/channel-runtime" and only overrides recordInboundSession.
  • Two new test suites cover the helper in isolation and the message-context named-account path end-to-end.

Known remaining gaps (acknowledged in PR risk section):

  • The Telegram reaction handler (bot-handlers.ts:875) still uses resolveAgentRoute + route.sessionKey directly without senderId, so reaction system events will still target the wrong session for named-account DMs.
  • The plugin-command handler in bot-native-commands.ts:861 passes sessionKeyForInternalHooks: route.sessionKey (raw), which may record hook events on the wrong session for named-account DMs. Both are reasonable candidates for a follow-up PR.

Confidence Score: 4/5

  • Safe to merge; the targeted fix is correct and well-tested, with two acknowledged residual code paths left for follow-up.
  • The core fix (extracting and using resolveTelegramConversationBaseSessionKey) is logically sound and directly mirrors the old inline logic. Tests cover the helper in isolation, the message-context path end-to-end, DM topic thread suffixing, identity-link canonicalization, and cross-account isolation. The score stops at 4 rather than 5 because two call sites in bot-handlers.ts (reaction handler) and bot-native-commands.ts (plugin-command sessionKeyForInternalHooks) still read route.sessionKey directly and were deliberately left out of this PR's scope — the PR itself notes this risk.
  • extensions/telegram/src/bot-handlers.ts (reaction handler at line 875) and extensions/telegram/src/bot-native-commands.ts (plugin-command path at line 861) both contain residual route.sessionKey usages that could still produce incorrect session routing for named-account DMs.

Comments Outside Diff (2)

  1. extensions/telegram/src/bot-handlers.ts, line 875 (link)

    P2 Residual raw route.sessionKey in reaction handler

    This reaction handler still reads route.sessionKey directly and also uses resolveAgentRoute (rather than resolveTelegramConversationRoute), so it will route system events to agent:main:main instead of agent:main:telegram:<account>:direct:<chatId> when a named-account DM receives a reaction.

    The PR's own risk section calls out: "another Telegram code path may still read route.sessionKey directly" — this line is exactly that case. The reaction handler doesn't have a senderId available from the reaction update, so migrating it requires a small refactor (use resolveTelegramConversationRoute with senderId obtained from the reaction's user field, then call resolveTelegramConversationBaseSessionKey).

    This is intentionally out of scope for the current PR, but worth a follow-up ticket to keep the pattern consistent across all Telegram event handlers.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: extensions/telegram/src/bot-handlers.ts
    Line: 875
    
    Comment:
    **Residual raw `route.sessionKey` in reaction handler**
    
    This reaction handler still reads `route.sessionKey` directly and also uses `resolveAgentRoute` (rather than `resolveTelegramConversationRoute`), so it will route system events to `agent:main:main` instead of `agent:main:telegram:<account>:direct:<chatId>` when a named-account DM receives a reaction.
    
    The PR's own risk section calls out: _"another Telegram code path may still read `route.sessionKey` directly"_ — this line is exactly that case. The reaction handler doesn't have a `senderId` available from the reaction update, so migrating it requires a small refactor (use `resolveTelegramConversationRoute` with `senderId` obtained from the reaction's `user` field, then call `resolveTelegramConversationBaseSessionKey`).
    
    This is intentionally out of scope for the current PR, but worth a follow-up ticket to keep the pattern consistent across all Telegram event handlers.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. extensions/telegram/src/bot-native-commands.ts, line 861 (link)

    P2 sessionKeyForInternalHooks still uses raw route.sessionKey

    For plugin command execution, sessionKeyForInternalHooks is set to the raw route.sessionKey rather than the normalized base session key from resolveTelegramConversationBaseSessionKey. For a named-account DM topic, this will point at agent:main:main instead of the correct per-account session key.

    The surrounding /status command path (line 656) was fixed in this PR; the plugin-command path just above was not. Depending on what sessionKeyForInternalHooks gates (e.g. hook recording, sub-agent announce routing), this may silently accumulate events on the wrong session.

    Consider applying the same resolveTelegramConversationBaseSessionKey call here, or tracking this as a follow-up alongside the reaction-handler gap noted above.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: extensions/telegram/src/bot-native-commands.ts
    Line: 861
    
    Comment:
    **`sessionKeyForInternalHooks` still uses raw `route.sessionKey`**
    
    For plugin command execution, `sessionKeyForInternalHooks` is set to the raw `route.sessionKey` rather than the normalized base session key from `resolveTelegramConversationBaseSessionKey`. For a named-account DM topic, this will point at `agent:main:main` instead of the correct per-account session key.
    
    The surrounding `/status` command path (line 656) was fixed in this PR; the plugin-command path just above was not. Depending on what `sessionKeyForInternalHooks` gates (e.g. hook recording, sub-agent announce routing), this may silently accumulate events on the wrong session.
    
    Consider applying the same `resolveTelegramConversationBaseSessionKey` call here, or tracking this as a follow-up alongside the reaction-handler gap noted above.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/telegram/src/bot-handlers.ts
Line: 875

Comment:
**Residual raw `route.sessionKey` in reaction handler**

This reaction handler still reads `route.sessionKey` directly and also uses `resolveAgentRoute` (rather than `resolveTelegramConversationRoute`), so it will route system events to `agent:main:main` instead of `agent:main:telegram:<account>:direct:<chatId>` when a named-account DM receives a reaction.

The PR's own risk section calls out: _"another Telegram code path may still read `route.sessionKey` directly"_ — this line is exactly that case. The reaction handler doesn't have a `senderId` available from the reaction update, so migrating it requires a small refactor (use `resolveTelegramConversationRoute` with `senderId` obtained from the reaction's `user` field, then call `resolveTelegramConversationBaseSessionKey`).

This is intentionally out of scope for the current PR, but worth a follow-up ticket to keep the pattern consistent across all Telegram event handlers.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/telegram/src/bot-native-commands.ts
Line: 861

Comment:
**`sessionKeyForInternalHooks` still uses raw `route.sessionKey`**

For plugin command execution, `sessionKeyForInternalHooks` is set to the raw `route.sessionKey` rather than the normalized base session key from `resolveTelegramConversationBaseSessionKey`. For a named-account DM topic, this will point at `agent:main:main` instead of the correct per-account session key.

The surrounding `/status` command path (line 656) was fixed in this PR; the plugin-command path just above was not. Depending on what `sessionKeyForInternalHooks` gates (e.g. hook recording, sub-agent announce routing), this may silently accumulate events on the wrong session.

Consider applying the same `resolveTelegramConversationBaseSessionKey` call here, or tracking this as a follow-up alongside the reaction-handler gap noted above.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 1a02e13

kimptoc pushed a commit to kimptoc/openclaw that referenced this pull request Mar 17, 2026
nikolaisid pushed a commit to nikolaisid/openclaw that referenced this pull request Mar 18, 2026
ssfdre38 pushed a commit to ssfdre38/openclaw-community-edition that referenced this pull request Mar 18, 2026
sbezludny pushed a commit to sbezludny/openclaw that referenced this pull request Mar 27, 2026
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 28, 2026
(cherry picked from commit 1eb810a)

# Conflicts:
#	extensions/telegram/src/bot-message-context.ts
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phantom duplicate session key causes /status to show 0 tokens when real session has data

2 participants