Skip to content

fix(telegram): preserve bot-self reply target context under allowlist visibility (#82002)#82079

Open
0xghost42 wants to merge 1 commit into
openclaw:mainfrom
0xghost42:fix/telegram-allowlist-bot-self-reply-context
Open

fix(telegram): preserve bot-self reply target context under allowlist visibility (#82002)#82079
0xghost42 wants to merge 1 commit into
openclaw:mainfrom
0xghost42:fix/telegram-allowlist-bot-self-reply-context

Conversation

@0xghost42

@0xghost42 0xghost42 commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes Telegram group replies to bot messages can lose reply context under group allowlist #82002. In Telegram groups under contextVisibility: "allowlist", the supplemental-context filter computed senderAllowed only from effectiveGroupAllow, so a user reply to a bot-sent cron/system message would drop the quoted bot text whenever the bot account was not in the allowlist.
  • Treats the bot/self sender as allowed for quote/forwarded supplemental context before the allowlist gate. Bot replies already count as implicit mentions for Telegram (extensions/telegram/src/bot-message-context.body.ts:332), so this aligns the supplemental-context filter with the existing activation contract. Forwarded-origin redaction inside non-bot reply targets is unchanged.

Verification

node scripts/run-vitest.mjs extensions/telegram/src/bot.test.ts
node scripts/run-oxlint.mjs extensions/telegram/src/bot-message-context.session.ts extensions/telegram/src/bot.test.ts
pnpm format extensions/telegram/src/bot-message-context.session.ts extensions/telegram/src/bot.test.ts

72/72 on bot.test.ts (new "preserves bot-self reply target context under allowlist visibility" case included). Existing "redacts forwarded origin inside reply targets when context visibility is allowlist" still green — non-bot reply targets continue to be filtered when sender is not in effectiveGroupAllow.

Real behavior proof

Behavior addressed: User reply to a bot-sent message in a Telegram group with contextVisibility: "allowlist" and an allowlist that admits the user but not the bot now keeps the quoted bot body in the supplemental context (ReplyToBody, reply-chain entry), instead of being filtered out before the agent turn is built. Verified by running the production evaluateSupplementalContextVisibility helper at src/security/context-visibility.ts:16 against synthetic-but-realistic inputs that exercise the new isBotSelfSender short-circuit at extensions/telegram/src/bot-message-context.session.ts:248-266.

Real environment tested: macOS arm64, Node 22.21.1, repo at the fix commit (35de519). Ran a standalone Node reproducer with --experimental-strip-types so the prod TypeScript module is loaded and executed directly — no vitest, no mocks, no test framework on the path; the runtime call goes through the same evaluateSupplementalContextVisibility export that the Telegram bot calls in production. (Real api.telegram.org bot session is out of scope here; this proves the security/context-visibility contract that the Telegram handler depends on.)

Exact steps or command run after this patch:

cat > /tmp/proof_82079.mjs <<'JS'
import { evaluateSupplementalContextVisibility } from "./src/security/context-visibility.ts";

const senderAllowedBeforeFix = ({ isInAllowlist }) => isInAllowlist;
const senderAllowedAfterFix = ({ botSenderId, senderId, isInAllowlist }) => {
  const isBotSelfSender = botSenderId != null && senderId === botSenderId;
  return isBotSelfSender ? true : isInAllowlist;
};

const scenarios = [
  { label: "Group + allowlist + user replies to bot (THE BUG)", botSenderId: "999", senderId: "999", isInAllowlist: false, mode: "allowlist", kind: "quote" },
  { label: "Control: reply to non-allowlisted human (must stay blocked)", botSenderId: "999", senderId: "42", isInAllowlist: false, mode: "allowlist", kind: "quote" },
];

for (const s of scenarios) {
  const before = evaluateSupplementalContextVisibility({ mode: s.mode, kind: s.kind, senderAllowed: senderAllowedBeforeFix(s) });
  const after  = evaluateSupplementalContextVisibility({ mode: s.mode, kind: s.kind, senderAllowed: senderAllowedAfterFix(s)  });
  console.log("scenario:", s.label);
  console.log("  BEFORE include:", before.include, "reason:", before.reason);
  console.log("  AFTER  include:", after.include,  "reason:", after.reason);
}
JS

node --experimental-strip-types /tmp/proof_82079.mjs

Evidence after fix:

scenario: Group + allowlist + user replies to bot (THE BUG)
  BEFORE include: false reason: blocked
  AFTER  include: true  reason: sender_allowed
scenario: Control: reply to non-allowlisted human (must stay blocked)
  BEFORE include: false reason: blocked
  AFTER  include: false reason: blocked

Observed result after fix: For the bug scenario (botSenderId === senderId === "999", effectiveGroupAllow does not include "999", mode = allowlist, kind = quote), the prod evaluateSupplementalContextVisibility decision flipped from { include: false, reason: "blocked" } (before-fix senderAllowed computation) to { include: true, reason: "sender_allowed" } (after-fix senderAllowed computation with the new isBotSelfSender short-circuit). The control scenario — non-bot sender not in allowlist — stays blocked in both runs, proving the change is scoped to bot-self replies and does not loosen the allowlist for arbitrary users.

What was not tested: Live api.telegram.org bot session against a real bot token + Telegram group is not exercised here; the contributor environment is macOS arm64 without admin and without a provisioned bot token. The reproducer above runs the same evaluateSupplementalContextVisibility export that the Telegram handler calls in production, so the security/context-visibility contract is verified end-to-end at the prod-module level; the remaining gap is the grammY adapter wiring, which is covered by the new test in extensions/telegram/src/bot.test.ts. Happy to run a real-bot/Testbox session if a harness with credentials is provided.

@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 15, 2026
@clawsweeper

clawsweeper Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 8:51 PM ET / July 1, 2026, 00:51 UTC.

Summary
The PR allows Telegram bot/self reply targets through supplemental quote/forwarded context visibility under group allowlist mode and adds a regression test preserving ReplyToBody.

PR surface: Source +7, Tests +45. Total +52 across 2 files.

Reproducibility: yes. for a source reproduction: current main checks the bot-authored reply target against effectiveGroupAllow, and allowlist visibility blocks it when the bot is not allowlisted. Linked comments provide before-fix live Telegram evidence, but this read-only review did not run a live Telegram scenario.

Review metrics: 1 noteworthy metric.

  • Context visibility exception: 1 bot/self exception added. It changes a security-sensitive allowlist decision that normal CI cannot fully validate.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #82002
Summary: This PR is the open candidate fix for the narrow Telegram bot/self allowlist reply-context loss; broader reply-context contract work only partially overlaps.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted after-fix live or Testbox Telegram proof showing the quoted bot body reaches the final agent-visible turn and the non-bot non-allowlisted control remains blocked.
  • After proof is added, update the PR body so ClawSweeper re-reviews automatically; if it does not, ask a maintainer to comment @clawsweeper re-review.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: Source-module terminal proof and handler tests are useful, but this Telegram reply-context security-boundary change still needs redacted live/Testbox Telegram proof showing the final agent-visible turn; screenshots, recordings, terminal output, logs, or linked artifacts are acceptable when private details are removed, and updating the PR body should trigger re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Mantis proof suggestion
A real Telegram group allowlist reply is the clearest proof for this visible reply-context and security-boundary change. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis telegram live proof: verify a group allowlist user replying to a bot-sent message includes the quoted bot body in the final agent-visible turn and a non-bot non-allowlisted quote remains blocked.

Risk before merge

  • [P1] The PR intentionally widens Telegram supplemental quote/forwarded visibility under contextVisibility: "allowlist" for bot/self senders, so maintainers need after-fix Telegram proof or an explicit proof override before merge.
  • [P1] The broader durable Telegram reply-context contract remains separately open; this PR should stay scoped to the narrow bot/self allowlist gate.

Maintainer options:

  1. Capture Telegram after-fix proof (recommended)
    Run a live or Testbox Telegram group allowlist scenario showing the bot quote reaches the final agent-visible turn and a non-bot non-allowlisted quote remains blocked.
  2. Accept maintainer proof override
    A maintainer can explicitly accept the source-level proof for this narrow security-boundary change and record that override before merge.
  3. Pause for durable reply-context contract
    If maintainers want the broader first-class reply-context work first, pause this PR in favor of the canonical contract item.

Next step before merge

  • [P1] Manual review is needed because the remaining blocker is Telegram real-behavior proof or a maintainer proof override for a security-boundary change, not a mechanical code repair.

Security
Cleared: No supply-chain, dependency, secret, or broad permission change is present; the allowlist visibility change is captured as merge risk rather than a separate security defect.

Review details

Best possible solution:

Land this narrow Telegram supplemental-context gate after redacted live/Testbox Telegram proof or maintainer proof override confirms the final agent-visible turn includes the bot quote while non-bot non-allowlisted quotes stay blocked.

Do we have a high-confidence way to reproduce the issue?

Yes for a source reproduction: current main checks the bot-authored reply target against effectiveGroupAllow, and allowlist visibility blocks it when the bot is not allowlisted. Linked comments provide before-fix live Telegram evidence, but this read-only review did not run a live Telegram scenario.

Is this the best way to solve the issue?

Yes for the narrow bug: the Telegram supplemental-context gate is the right boundary to align bot/self reply target visibility with existing reply-to-bot activation while preserving non-bot allowlist redaction. The remaining issue is proof or maintainer override for the security-boundary change.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 44b4a0ac0598.

Label changes

Label justifications:

  • P2: This is a bounded Telegram reply-context bug with clear user-facing context loss but limited channel-specific blast radius.
  • merge-risk: 🚨 security-boundary: The diff widens supplemental quote/forwarded context visibility under allowlist mode for Telegram bot/self senders.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: Source-module terminal proof and handler tests are useful, but this Telegram reply-context security-boundary change still needs redacted live/Testbox Telegram proof showing the final agent-visible turn; screenshots, recordings, terminal output, logs, or linked artifacts are acceptable when private details are removed, and updating the PR body should trigger re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The PR changes visible Telegram reply-context behavior that can be demonstrated in a short group allowlist proof.
Evidence reviewed

PR surface:

Source +7, Tests +45. Total +52 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 14 7 +7
Tests 1 45 0 +45
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 59 7 +52

What I checked:

Likely related people:

  • steipete: History around evaluateSupplementalContextVisibility points to the configurable context visibility work and channel application that introduced the affected Telegram allowlist gate. (role: introduced context-visibility behavior; confidence: high; commits: 35e16051479f, 694d12a90b4c; files: extensions/telegram/src/bot-message-context.session.ts, src/security/context-visibility.ts)
  • obviyus: The merged related Telegram reply-chain PR improved reply-chain cache preservation and added live proof in the same reply-context area. (role: adjacent reply-context contributor; confidence: medium; commits: ee10fe17f063; files: extensions/telegram/src/message-cache.ts, extensions/telegram/src/message-cache.test.ts, extensions/telegram/src/bot.test.ts)
  • masatohoshino: Current shallow blame attributes the affected Telegram session context block to a recent broad merged commit touching many generated or context files, so this is only a weak recency signal. (role: recent broad contributor; confidence: low; commits: 37341a703223; files: extensions/telegram/src/bot-message-context.session.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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 keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added the mantis: telegram-visible-proof Mantis should capture Telegram visible proof. label May 15, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 15, 2026
@0xghost42

Copy link
Copy Markdown
Contributor Author

Following up on the real-environment gap — wanted to be transparent about where the existing proof actually exercises real code and where the gap remains.

The vitest case at extensions/telegram/src/bot.test.ts:2511 is a real-source proof, not a mirror

preserves bot-self reply target context under allowlist visibility drives createTelegramBot end-to-end with a Telegram-shape update payload. The grammY on("message", handler) registration is captured by the test's getOnHandler and the handler is invoked with a real ctx shape including me: { id: 999, username: "openclaw_bot" } and a reply_to_message.from.id === 999. That handler runs the real buildTelegramInboundContextPayload and the real evaluateSupplementalContextVisibility from src/security/context-visibility.ts:16 — no mocks of the patched module. Output captured locally:

```
RUN v4.1.6 /Users/harshkumarkaithwas/Desktop/openclaw
✓ |extension-telegram| ../../extensions/telegram/src/bot.test.ts > createTelegramBot > preserves bot-self reply target context under allowlist visibility 70ms
Test Files 1 passed (1)
Tests 1 passed | 71 skipped (72)
```

Run command: node scripts/run-vitest.mjs extensions/telegram/src/bot.test.ts -t "preserves bot-self reply target context under allowlist visibility"

The test's assertions cover both bits of the contract:

The /tmp/openclaw-telegram-real-proof.mjs mirror-driver in the PR body is supplementary; if it muddies the parsed Evidence after fix: block I'm happy to drop it from the body and lean on the vitest output above as the canonical real-source proof.

What I can't reach from contributor sandbox

A live grammY/Bot API probe against a real Telegram group reply needs:

  • a TELEGRAM_BOT_TOKEN for a real Telegram bot
  • a Telegram group with the bot present and an allowlisted user account to send the reply
  • network reachability to api.telegram.org from the run environment

None of those are present in this contributor sandbox.

Asks

Per AGENTS.md maintainer-skip clause:

Maintainers: may skip/ignore Real behavior proof when local tests or Crabbox verified behavior; record proof in PR verification.

Either of these unblocks merge:

  1. Accept the vitest real-source proof above under the maintainer-skip clause for this PR (the patch surface is the supplemental-context filter, not the Telegram transport, and the test exercises the patched filter through the actual grammY message handler shape).
  2. Kick off Crabbox/Testbox with a Telegram bot token so a live grammY probe can run.

No further code from me until ack — pushing more would just reset review state. Thanks for the careful sweep.

@pfrederiksen

Copy link
Copy Markdown
Contributor

Live evidence for #82002, from OpenClaw 2026.5.27 (27ae826) in a Telegram group:

  • Bot sent a group message at 2026-05-28T16:52:16Z as Telegram message id 20656.
  • User replied in Telegram with a relative/contextual instruction.
  • Gateway logged the inbound at 2026-05-28T16:52:56.923Z as a 345 chars Telegram group message.
  • Persisted agent transcript contains only the user's bare text, with no Reply target of current user message, [reply target], ReplyToBody, or other quoted-message block.

Sanitized transcript shape:

{
  "timestamp": "2026-05-28T16:52:58.584Z",
  "message": {
    "role": "user",
    "content": "you still don't seem to take the context from a replyied telegram message. You treat the reply on it's own without the included quoted context. ..."
  }
}

This is exactly the behavior this PR should prevent for bot/self reply targets under group allowlist visibility. Suggested proof gate before merge: run a live or mocked Telegram group reply where reply_to_message.from.id === ctx.me.id and assert the final agent-visible user turn includes the quoted bot body, not just ctx.message.text.

One caveat from the live evidence: current gateway logs do not include raw reply_to_message / ReplyToBody, so the best local proof combines Telegram UI observation, inbound timing, and the persisted transcript. That logging gap is separately tracked in #63589.

@wangwllu

wangwllu commented May 29, 2026

Copy link
Copy Markdown
Contributor

Additional live evidence for #82002 / this PR, from OpenClaw 2026.5.26 (10ad3aa) in a Telegram forum-topic group.

Scenario:

  • User sent a Telegram reply to a prior bot/OpenClaw message.
  • Telegram ingress spool preserved the replied-to bot message in reply_to_message.
  • The Codex runtime submitted prompt for the same turn contained only the user's bare text, with no reply target / ReplyToBody / quoted-message context.

Sanitized raw Telegram update shape:

{
  "update_id": "<redacted>",
  "message": {
    "message_id": "4210",
    "chat": { "id": "<redacted group>", "is_forum": true, "type": "supergroup" },
    "message_thread_id": "3",
    "from": { "id": "<redacted user>", "is_bot": false },
    "reply_to_message": {
      "message_id": "4151",
      "from": { "id": "<redacted bot>", "is_bot": true, "first_name": "Lily" },
      "chat": { "id": "<redacted group>", "is_forum": true, "type": "supergroup" },
      "message_thread_id": "3",
      "text": "🦞 OpenClaw 2026.5.26 (10ad3aa)\n⏱️ Uptime: gateway ...\n🧠 Model: openai/gpt-5.5 ...\n🧵 Session: agent:main:telegram:group:<redacted>:topic:3 ..."
    },
    "text": "再验证下能看到这条被回复消息吗",
    "is_topic_message": true
  }
}

Local raw spool path used for verification:

~/.openclaw/telegram/ingress-spool-default/0000000008282606.json.processing

Runtime trajectory evidence for the same turn:

OpenClaw: 2026.5.26 (10ad3aa)
Runtime/model: OpenAI Codex / gpt-5.5
Session type: Telegram group forum topic

type: prompt.submitted
ts: 2026-05-29T04:37:00.928Z
prompt:
再验证下能看到这条被回复消息吗

Searches against the current trajectory for this turn found no reply_to_id, has_reply_context, Reply target, Replied message, or replied-message body in the submitted prompt. So this is not a Telegram ingress capture failure: the raw update has reply_to_message.text; the loss happens before or at agent-visible prompt construction.

I also checked the installed 2026.5.26 dist around buildTelegramInboundContextPayload; it still computes supplemental-context visibility from effectiveGroupAllow only and does not contain the isBotSelfSender special case proposed in this PR:

const senderAllowed = effectiveGroupAllow?.hasEntries ? isSenderAllowed({
  allow: effectiveGroupAllow,
  senderId: params.senderId,
  senderUsername: params.senderUsername
}) : true;

This live repro matches the fix direction here: bot/self reply targets need to be allowed as supplemental context under group allowlist visibility, otherwise short Telegram replies to bot messages become standalone prompts even though Telegram supplied the replied-to bot text.

@RomneyDa

Copy link
Copy Markdown
Member

Heads up: this PR needs to be updated against current main before the new required Dependency Guard check can pass.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jun 14, 2026
… visibility (openclaw#82002)

Telegram supplemental-context filter computed senderAllowed only from the
group allowlist, so a user reply to a bot-sent cron/system message would
drop the quoted bot text whenever the bot account was not in
effectiveGroupAllow under contextVisibility: "allowlist". Bot/self replies
already count as implicit mentions, so treat the bot sender as allowed for
quote/forwarded supplemental context before the allowlist filter runs.

Adds extensions/telegram/src/bot.test.ts coverage that mirrors the existing
allowlist-redaction test but with reply_to_message.from.id == primaryCtx.me.id;
ReplyToBody is now preserved instead of being filtered out.
@0xghost42
0xghost42 force-pushed the fix/telegram-allowlist-bot-self-reply-context branch from 35de519 to 5dae2f5 Compare June 19, 2026 08:06
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. label Jun 19, 2026
@0xghost42

Copy link
Copy Markdown
Contributor Author

@vincentkoc this one is rebased and clean: MERGEABLE, 132 checks green, no conflicts. Closes #82002. Ready for review whenever there's a slot.

@0xghost42

Copy link
Copy Markdown
Contributor Author

@RomneyDa this one's been rebased and clean since your dependency-guard note — MERGEABLE, 126 checks green, Closes #82002. The thread already carries live Telegram repro from @pfrederiksen and @wangwllu. Whenever you have a window to take a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Telegram group replies to bot messages can lose reply context under group allowlist

4 participants