fix(msteams): surface quoted message body in Teams quote replies#101856
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 9, 2026, 7:19 AM ET / 11:19 UTC. Summary PR surface: Source +71, Tests +216. Total +287 across 9 files. Reproducibility: yes. at source level: current main only matches Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the Teams plugin fix after required exact-head checks, keeping full-text Graph quote recovery DM-only and leaving group/channel quote context on the visibility-safe preview path. Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main only matches Is this the best way to solve the issue? Yes, this is the best supported fix shape I found: parse Teams-owned quote HTML in the inbound adapter, recover full text only for 1:1 DMs, and keep group/channel behavior on the preview path behind existing supplemental-context filtering. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 7abf1c7f85a5. Label changesLabel justifications:
Evidence reviewedPR surface: Source +71, Tests +216. Total +287 across 9 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
Review history (5 earlier review cycles)
|
|
@clawsweeper re-review — updated the PR body with real-behavior proof: before/after parser output against the exact Teams payload, and two redacted 1:1 DM screenshots showing the bot reading back the full quoted message. Also addressed the review blockers in 160898a (Graph full-text fetch restricted to DMs, unsupported $select removed, graph-thread mock factories updated + a group-chat gate regression test). |
|
🦞🧹 I asked ClawSweeper to review this item again. |
035a84e to
fabfe62
Compare
Teams sends the quoted text of a 1:1 DM quote-reply in <p itemprop="preview">
(a truncated snippet), not <p itemprop="copy"> which the parser matched. The
match failed, so quoteInfo was undefined and nothing was surfaced to the agent.
Fix 1 (primary): extractMSTeamsQuoteInfo now accepts copy OR preview (prefers
copy, falls back to preview) and captures the blockquote itemid as the quoted
message id.
Fix 2 (enhancement): when the quoted message id is known, fetch the full text
via the app-only Graph endpoint GET /chats/{chatId}/messages/{id} (permitted
with Chat.Read.All, unlike the delegated /me/chats listing) and use it as the
quote body. Restricted to chats (DM + group) whose Graph chat id is a 19: id;
any failure degrades to the truncated preview so message handling never breaks.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
…ted $select, fix mocks Addresses ClawSweeper review on the quote-reply PR: - Security (P1): restrict the app-only Graph full-text quote fetch to 1:1 DMs. Previously it ran for any non-channel chat, so in a group an allowlisted sender could quote a non-allowlisted member and the fetched full body would bypass the supplemental-quote visibility allowlist. Group/channel quotes keep the (now-surfaced) truncated preview from fix 1. - Graph contract (P2): the get-chatMessage endpoint does not support OData query params; drop the ?$select=id,body that tenants enforcing the contract would reject (which would silently fall back to the preview). - Tests (P1): add fetchChatMessageText to the two vi.mock(../graph-thread.js) factories prod now touches, and add a group-chat regression test proving the Graph full-text fetch does not fire for group quotes. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Real-behavior evidence for the quote-reply fix (personal names + avatars redacted): a 1:1 DM where the bot now reads back the full quoted message. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Proof media should live as external PR artifacts, not in the product tree. Co-Authored-By: Claude Opus 4.8 <[email protected]>
fabfe62 to
0ea5402
Compare
|
Maintainer review/proof complete.
Known proof gap: I did not have a locally controlled Microsoft 365 tenant. The contributor's current-head Real behavior proof supplies native tenant/gateway evidence; the maintainer replay independently covers parsing, privacy gating, Graph routing, deadline, and fallback behavior. |
|
Merged via squash.
|
…nclaw#101856) * fix(msteams): surface quoted message body in Teams quote replies Teams sends the quoted text of a 1:1 DM quote-reply in <p itemprop="preview"> (a truncated snippet), not <p itemprop="copy"> which the parser matched. The match failed, so quoteInfo was undefined and nothing was surfaced to the agent. Fix 1 (primary): extractMSTeamsQuoteInfo now accepts copy OR preview (prefers copy, falls back to preview) and captures the blockquote itemid as the quoted message id. Fix 2 (enhancement): when the quoted message id is known, fetch the full text via the app-only Graph endpoint GET /chats/{chatId}/messages/{id} (permitted with Chat.Read.All, unlike the delegated /me/chats listing) and use it as the quote body. Restricted to chats (DM + group) whose Graph chat id is a 19: id; any failure degrades to the truncated preview so message handling never breaks. Co-Authored-By: Claude Opus 4.8 <[email protected]> * fix(msteams): address review — DM-only full-text fetch, drop unsupported $select, fix mocks Addresses ClawSweeper review on the quote-reply PR: - Security (P1): restrict the app-only Graph full-text quote fetch to 1:1 DMs. Previously it ran for any non-channel chat, so in a group an allowlisted sender could quote a non-allowlisted member and the fetched full body would bypass the supplemental-quote visibility allowlist. Group/channel quotes keep the (now-surfaced) truncated preview from fix 1. - Graph contract (P2): the get-chatMessage endpoint does not support OData query params; drop the ?$select=id,body that tenants enforcing the contract would reject (which would silently fall back to the preview). - Tests (P1): add fetchChatMessageText to the two vi.mock(../graph-thread.js) factories prod now touches, and add a group-chat regression test proving the Graph full-text fetch does not fire for group quotes. Co-Authored-By: Claude Opus 4.8 <[email protected]> * docs(msteams): add redacted real Teams quote-reply proof screenshots Real-behavior evidence for the quote-reply fix (personal names + avatars redacted): a 1:1 DM where the bot now reads back the full quoted message. Co-Authored-By: Claude Opus 4.8 <[email protected]> * docs(msteams): remove committed proof screenshots from branch Proof media should live as external PR artifacts, not in the product tree. Co-Authored-By: Claude Opus 4.8 <[email protected]> * test(msteams): bound and prove quote enrichment * test(msteams): use valid open DM fixture --------- Co-authored-by: Yash Inani <[email protected]> Co-authored-by: Claude Opus 4.8 <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
What Problem This Solves
When a user quote-replies to the bot's message in a Microsoft Teams 1:1 DM, the bot could not see the quoted message content — it only saw the user's new text.
Root cause: Teams delivers the quoted text of a quote-reply inside
<p itemprop="preview">…</p>(a truncated snippet), butextractMSTeamsQuoteInfoonly matched<p itemprop="copy">…</p>. The match failed,quoteInfocame backundefined, and nothing was surfaced to the agent viasupplemental.quote.Why This Change Was Made
extractMSTeamsQuoteInfonow matches<p itemprop="copy">or<p itemprop="preview">(preferscopy, falls back topreview) and captures the blockquoteitemidas the quoted message id.previewsnippet is Teams-truncated, so when the quoted message id is known and the conversation is a 1:1 DM, fetch the complete text via the app-only endpointGET /chats/{chatId}/messages/{id}(Chat.Read.All) and use it as the quote body. Restricted to DMs so a fetched body cannot bypass the group supplemental-quote visibility allowlist. Any failure degrades to the truncated preview, so message handling never breaks.User Impact
Teams users who quote-reply the bot in a 1:1 DM now get responses that account for the quoted message instead of ignoring it. Group and channel quote replies surface the previously-dropped preview snippet. No configuration changes; no impact on non-quote messages.
Evidence
Parser, against the exact Teams quote-reply payload (redacted):
Live in a real Teams tenant (1:1 DM; redacted screenshots shared with reviewers, kept as external artifacts rather than committed): the bot now reads back the full quoted message instead of only the new text. Also deployed live on a production Teams gateway (
@openclaw/msteams2026.6.6): the gateway restarted clean ([msteams] starting provider) and a real quote-reply surfaces the quoted body to the agent.Validation: msteams suites 85 passing (incl. a group-chat gate regression test);
pnpm tsgo:extensionsandpnpm tsgo:extensions:testclean.🤖 Generated with Claude Code