fix(telegram): skip chat_window context for persistent DMs already in session transcript (#87566)#87626
fix(telegram): skip chat_window context for persistent DMs already in session transcript (#87566)#87626draix wants to merge 1 commit into
Conversation
… session transcript (openclaw#87566) Telegram private DMs that route to a persistent OpenClaw session were receiving a duplicated "Conversation context (untrusted, chronological, selected for current message)" block on every inbound user turn. The block contains the last ~10 cached Telegram messages, all of which are already in the session transcript for a persistent DM session — so the model sees the same history twice and burns extra tokens / cache writes on every turn. Fix: filter chat_window entries out of the Telegram supplemental untrustedContext when ALL of the following hold: - The chat is a Telegram private DM (no group, no topic/thread). - The session has prior activity (readSessionUpdatedAt returned a timestamp), i.e. it's a real persistent transcript and not a first-touch fresh session. The fix preserves: - Group / supergroup / channel chats. - Forum / topic messages (message_thread_id present). - Reply / quote / forward context (handled separately via supplemental.quote, supplemental.forwarded, and extra.ReplyChain). - Fresh / never-used persistent sessions (first inbound still gets chat_window so the model has the cached recent messages). No new config knob is introduced; the new behavior is default-correct for the issue reporter's repro and the clawsweeper review. Fixes openclaw#87566
|
Codex review: needs real behavior proof before merge. Reviewed May 29, 2026, 1:09 AM ET / 05:09 UTC. Summary PR surface: Source +86, Tests +420, Docs +1. Total +507 across 6 files. Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Review detailsBest possible solution: Retry the Codex review after fixing the execution failure. Do we have a high-confidence way to reproduce the issue? Unclear. The review failed before ClawSweeper could establish a reproduction path. Is this the best way to solve the issue? Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction. AGENTS.md: unclear because the file could not be read completely. Codex review notes: model gpt-5.5, reasoning high; reviewed against 5fb83af3e389. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +86, Tests +420, Docs +1. Total +507 across 6 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
ClawSweeper PR egg: 🎁 locked until real behavior proof passes. Details
|
Fixes #87566
Summary
Telegram private DMs that route to a persistent OpenClaw session (e.g.
agent:main:main) were receiving a duplicatedConversation context (untrusted, chronological, selected for current message)block on every inbound user turn. The block contains the last ~10 cached Telegram messages — but those messages are already in the persistent session transcript, so the model saw the same history twice and paid for it twice on every turn.This change suppresses the
chat_windowprompt-context block when (and only when):readSessionUpdatedAtreturned a timestamp). Fresh sessions still get the chat_window.No new config knob is introduced; the new behaviour is default-correct for the issue's repro and matches the clawsweeper "narrow session-aware suppression" recommendation.
Root cause
extensions/telegram/src/bot-handlers.runtime.tsbuilds achat_windowentry from the Telegram message cache on every inbound message viabuildPromptContextForMessage→buildTelegramConversationContext({ recentLimit: 10, replyTargetWindowSize: 2 }), thenextensions/telegram/src/bot-message-context.session.tsunconditionally attaches it assupplemental.untrustedContext. The persistent session transcript already covers that history, so for 1:1 DMs the block is pure duplication.The fix lands at the attachment site in
bot-message-context.session.ts, where we already have:isGroup(from the route classifier)threadSpec.id(forum / DM topic id, if any)previousTimestamp = sessionRuntime.readSessionUpdatedAt({ storePath, sessionKey: route.sessionKey })— the per-session updated-at, which is exactly the "has this session already been written to?" signal we need to distinguish persistent-with-transcript from fresh.The filtering logic lives in a tiny pure helper (
bot-message-context.prompt-context-filter.ts) so it is unit-testable independent of the bot.Minimal reproduction
A two-turn Telegram DM into a persistent session (
agent:main:main):Before (every user turn duplicates recent history)
(Persistent session transcript already contains #6792 + #6793 → those two are now sent twice on every turn.)
After
Reply-chain / quote / forwarded context is preserved separately via
supplemental.quote,supplemental.forwarded, andextra.ReplyChain, so dropping thechat_windowdoes not lose reply context.Token-impact estimate
On long-lived 1:1 Telegram DMs the duplicated block grows linearly until it stabilises at
recentLimit: 10messages. With OpenClaw's typical 200–400-token envelope per cached message, that is ~2–4k extra tokens per inbound turn appended to user content (which also defeats prefix caching), in line with @piazzatron's "~5x token burn" comment on a 1:1 session that otherwise carries only a short transcript per turn. Group / topic / reply / fresh-session paths are unchanged.Architecture affected
Telegram inbound flow:
The decision lives entirely inside the Telegram extension; the channel-inbound formatter, prompt assembly, and session transcript paths are untouched.
Behaviour matrix
message_thread_id)ReplyChain/quoteTests
Two new test files exercise the fix; both fail without it and pass with it.
extensions/telegram/src/bot-message-context.prompt-context-filter.test.ts— 11 pure unit tests of the suppression decision and the entry filter (group vs private, topic vs no-topic, persistent vs fresh, mixed entry types, identity / no-op semantics, edge-case timestamps).extensions/telegram/src/bot.test.ts— 5 new integration tests underdescribe("Telegram chat_window suppression for persistent DMs (#87566)"):chat_windowinUntrustedStructuredContext.previousTimestampMs) →chat_windowpresent.chat_windowpresent.chat_windowsuppressed, butReplyChainpreserved.message_thread_id), persistent session →chat_windowpresent.Acceptance criteria —
node scripts/run-vitest.mjsoutputextensions/telegram/src/bot.test.ts:extensions/telegram/src/message-cache.test.ts:New filter unit tests:
Out of scope
dmHistoryLimitsemantics; the issue notes that knob is on a separate code path. The new behaviour is the default and needs no config opt-in or opt-out.agent:main:telegram:atlas:direct:alice-shared) are treated identically to default-account DMs because the suppression decision usespreviousTimestampon the resolved sessionKey, not the literalmainSessionKeyvalue.chat_windowbuild itself inbuildPromptContextForMessageis left intact (it is still cheap and used by groups/topics/fresh sessions); we only filter at the attachment site. This keeps the surgery minimal and the suppression decision in one place.CHANGELOG
Added under
## 2026.5.28→### Fixes.