Skip to content

feat(msteams): respond to channel messages by keyword without an @mention#92591

Open
colton-octaria wants to merge 1 commit into
openclaw:mainfrom
colton-octaria:msteams-mention-patterns
Open

feat(msteams): respond to channel messages by keyword without an @mention#92591
colton-octaria wants to merge 1 commit into
openclaw:mainfrom
colton-octaria:msteams-mention-patterns

Conversation

@colton-octaria

Copy link
Copy Markdown
Contributor

Summary

The MS Teams channel is the only group-capable channel that ignores messages.groupChat.mentionPatterns. Discord, Slack and WhatsApp each expose a per-provider mentionPatterns policy that scopes those patterns and treats a text match as an implicit mention — so a user can address the agent by name without an @. The msteams handler never wired this, so every non-@mention group/channel message is dropped at the requireMention gate.

This adds the same seam to msteams (no new concept):

  • configchannels.msteams.mentionPatterns (MentionPatternsPolicyConfig), identical in shape to channels.discord.mentionPatterns / slack / whatsapp. It scopes the global messages.groupChat.mentionPatterns to selected Teams conversations (allow/deny by conversation id).
  • handler — build the mention regexes (buildMentionRegexes(cfg, agentId, { provider: "msteams", conversationId, providerPolicy: msteamsCfg?.mentionPatterns })) and OR a match into the wasMentioned fact passed to resolveInboundMentionDecision. A matched message dispatches as if @mentioned; everything else still hits the existing requireMention skip, so there is no extra model turn for ordinary channel chatter. requireMention semantics are unchanged.
  • self-echo guard — with RSC ChannelMessage.Read.Group the bot also receives its own channel posts; the new isMSTeamsKeywordMention helper returns false when activity.from.id === activity.recipient.id, so a reply that contains the keyword cannot re-trigger the bot.
  • helperisMSTeamsKeywordMention in extensions/msteams/src/mentions.ts (mirrors Discord's mention-state resolution), unit-tested.

Config surface note

This adds one config field to an existing channel to reach parity with three sibling channels that already have it; the matching patterns still come from the existing global messages.groupChat.mentionPatterns. No default changes, no migration, fully backward-compatible (absent → today's behavior exactly).

Real behavior proof

Behavior addressed: A Microsoft Teams channel message that names the agent (matches a configured mention pattern, e.g. \bradar\b) without an @mention now gets a reply; a channel message that does not match still gets no reply and triggers no model turn; the bot does not trigger on its own posts.

Real environment tested: A live Microsoft Teams deployment (OpenClaw 2026.6.5, native msteams plugin) with the equivalent keyword-mention behavior applied to the installed plugin and RSC ChannelMessage.Read.Group consented, exercised by posting real messages in a Teams channel and reading the bot's edge (nginx) and gateway journals over ssh.

Exact steps or command run after this patch: Posted three top-level channel messages naming the agent without an @ ("Radar, can you hear me now?", "RADAR ... who am i", "cool... radar... can you give me latest oil price") and one with no keyword ("The weather today in Little Rock, is sunny and quite nice."), then read the inbound edge and gateway activity:

ssh <relay> "grep '/api/messages' /var/log/nginx/access.log | tail -1"
ssh <agent> "journalctl --user -u openclaw-gateway --since '-6 min' | grep -iE 'tool-policy|run agent'"

Evidence after fix: The inbound non-@ channel message reaches the bot, and only the keyword messages start an agent turn:

# nginx edge — Bot Framework delivered the non-@ channel message, bot accepted (200)
52.112.116.120 - - [13/Jun/2026:02:49:10 +0000] "POST /api/messages HTTP/1.1" 200 0 "-" "Microsoft-SkypeBotApi (Microsoft-BotFramework/3.0)"

# gateway journal — one agent turn per keyword message (03:02:47, 03:04:14, 03:04:35);
# the no-keyword "weather" message posted at 03:03 produced NO turn between them
03:02:47 [agents/tool-policy] tool policy removed 5 tool(s) via tools.profile (coding): ...
03:04:14 [agents/tool-policy] tool policy removed 5 tool(s) via tools.profile (coding): ...
03:04:35 [agents/tool-policy] tool policy removed 5 tool(s) via tools.profile (coding): ...

# the agent's actual channel replies (no @mention was used):
Radar: "Loud and clear, Colton. What do you need?"
Radar: "Loud and clear, Colton — you're Colton Williams, one of the REG admins."
Radar: <latest WTI / Brent crude prices>

Observed result after fix: All three keyword messages were answered in-channel without an @mention; the no-keyword weather message was received by the bot but produced no agent turn and no reply (the requireMention skip still fires for non-matches); the bot did not reply to its own keyword-containing replies. @mention and DM behavior were unchanged.

What was not tested: I exercised the behavior live via the equivalent keyword-mention logic applied to a deployed 2026.6.5 plugin rather than a from-source gateway build of this exact branch; the source on this branch is covered by the added isMSTeamsKeywordMention unit cases (match / case-insensitive / no-keyword / self-echo / DM / already-mentioned / empty-patterns / empty-text), which I ran green locally via the repo's scoped vitest helper. I could not run the full local pnpm check lanes (oxfmt / tsgo / broad suites) — the toolchain is not fully installed in my checkout. Private/shared-channel RSC variants were not exercised.

Screenshots of the live Teams thread are available on request (repo policy keeps proof images off branches).

@openclaw-barnacle openclaw-barnacle Bot added channel: msteams Channel integration: msteams size: S proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 13, 2026
@clawsweeper

clawsweeper Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge. Reviewed June 30, 2026, 7:51 PM ET / 23:51 UTC.

Summary
The branch adds opt-in Microsoft Teams mention-pattern config and runtime handling so keyword-matched channel/group messages count as mentions, plus unit coverage for the keyword helper.

PR surface: Source +59, Tests +51. Total +110 across 5 files.

Reproducibility: yes. for the review blocker: source inspection shows the PR adds a Teams config key while generated metadata, UI hints, and docs omit it. The Teams runtime behavior itself has contributor-supplied live log proof rather than a reviewer-run live reproduction.

Review metrics: 1 noteworthy metric.

  • Config surfaces: 1 added. channels.msteams.mentionPatterns is a new user-facing channel config key, so generated metadata, UI hints, docs, and upgrade expectations need review before merge.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦞 diamond lobster
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

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

Rank-up moves:

  • [P2] Add Teams mention-pattern UI hints, regenerate bundled channel config metadata, and update the relevant Teams/group docs.
  • [P2] Refresh the branch on current main after the config/docs repair.

Proof path suggestion
A short real Teams proof would materially help maintainers verify the visible keyword-reply and non-keyword-silence behavior in the actual transport. Mantis is currently scoped to Telegram, Discord, and web UI chat proof, so it is not the right proof path for this surface.
Use maintainer screenshot/manual proof, browser or Playwright proof, Crabbox where appropriate, or normal local artifact proof instead.

Risk before merge

  • [P1] Merging as-is would make channels.msteams.mentionPatterns runtime-valid while generated metadata, UI hints, and public docs omit it, leaving operators and config tooling with an incomplete contract.
  • [P1] The PR currently conflicts with current main, so the final merge needs a branch refresh after the config/docs repair rather than treating the old base as merge-ready.

Maintainer options:

  1. Align Teams Config Contract (recommended)
    Add Teams mention-pattern UI hints, regenerate bundled channel config metadata and config docs as needed, document the Teams allowIn/denyIn conversation ID shape, then refresh the branch on current main.
  2. Accept Runtime-First Drift
    Maintainers could intentionally land the runtime change first, but the config UI/help/docs contract would remain stale until a follow-up ships.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Add Microsoft Teams `mentionPatterns` UI hints, regenerate bundled channel config metadata and config docs baseline if needed, document `channels.msteams.mentionPatterns` and the Teams `allowIn`/`denyIn` conversation ID shape in the Teams/group channel docs, refresh against current main, and keep the runtime mention-gate behavior unchanged.

Next step before merge

  • [P2] A narrow repair can align Teams UI hints, generated metadata, and docs, then refresh the branch without changing the core runtime approach.

Security
Cleared: No concrete security or supply-chain concern found; the diff changes TypeScript config/runtime mention handling and tests without dependencies, workflows, lockfiles, secrets, or external execution sources.

Review findings

  • [P2] Align Teams mention-pattern config metadata and docs — src/config/types.msteams.ts:166-172
Review details

Best possible solution:

Land the shared Teams keyword-mention runtime seam only after Teams UI hints, generated channel metadata, public docs for Teams conversation IDs, and a current-main refresh are aligned.

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

Yes for the review blocker: source inspection shows the PR adds a Teams config key while generated metadata, UI hints, and docs omit it. The Teams runtime behavior itself has contributor-supplied live log proof rather than a reviewer-run live reproduction.

Is this the best way to solve the issue?

No, not complete yet. Reusing the shared mention-pattern seam is the right owner boundary, but the best fix also updates the Teams config contract surfaces and refreshes the branch before merge.

Full review comments:

  • [P2] Align Teams mention-pattern config metadata and docs — src/config/types.msteams.ts:166-172
    Adding channels.msteams.mentionPatterns exposes a new user-facing config key, but the PR does not add Teams UI hints, regenerate bundled channel metadata, or document the Teams allowIn/denyIn conversation ID shape. That leaves runtime validation ahead of the generated config/help contract, so operators and tooling can miss or misrepresent the option.
    Confidence: 0.88

Overall correctness: patch is incorrect
Overall confidence: 0.88

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5e572dcf781a.

Label changes

Label justifications:

  • P2: This is a useful Microsoft Teams channel improvement with limited blast radius, blocked by normal config/docs contract alignment rather than an urgent runtime outage.
  • merge-risk: 🚨 compatibility: The PR adds a runtime-accepted Teams config surface without updating generated metadata, UI hints, or public docs, creating configuration contract drift for operators and tooling.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (logs): The PR body supplies redacted live Microsoft Teams edge/gateway logs and observed replies/no-reply behavior for the keyword path, sufficient for the external PR proof gate despite not being a from-source run of this exact branch.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies redacted live Microsoft Teams edge/gateway logs and observed replies/no-reply behavior for the keyword path, sufficient for the external PR proof gate despite not being a from-source run of this exact branch.
Evidence reviewed

PR surface:

Source +59, Tests +51. Total +110 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 4 61 2 +59
Tests 1 52 1 +51
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 113 3 +110

Acceptance criteria:

  • [P1] node scripts/run-vitest.mjs extensions/msteams/src/mentions.test.ts extensions/msteams/src/monitor-handler/message-handler.authz.test.ts.
  • [P1] pnpm config:channels:check.
  • [P1] pnpm config:docs:check.
  • [P1] git diff --check.

What I checked:

Likely related people:

  • steipete: Authored the scoped mention-pattern policy/docs commits and the SDK export that this PR extends to Teams. (role: introduced related policy and adjacent docs owner; confidence: high; commits: 4cc940909ce7, d5edd28e1df9, 625fd5b3e3e2; files: src/channels/mention-pattern-policy.ts, src/auto-reply/reply/mentions.ts, src/plugin-sdk/channel-inbound.ts)
  • heyitsaamir: Authored the merged Teams SDK rebase that recently touched the Teams message handler, Teams config type, and Teams UI hints involved in this review. (role: recent Teams feature contributor; confidence: medium; commits: 04c29825356f; files: extensions/msteams/src/monitor-handler/message-handler.ts, src/config/types.msteams.ts, extensions/msteams/src/config-ui-hints.ts)
  • masatohoshino: The current main blame/log for the central Teams files points to a recent large commit that reintroduced the current Teams file versions, though the commit topic is broader than this Teams feature. (role: recent current-main carrier; confidence: low; commits: a75431c586ce; files: extensions/msteams/src/monitor-handler/message-handler.ts, src/config/types.msteams.ts, extensions/msteams/src/config-ui-hints.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.
Review history (1 earlier review cycle)
  • reviewed 2026-06-21T18:58:34.716Z sha 763dd83 :: needs changes before merge. :: [P2] Regenerate Teams config metadata and docs

@clawsweeper clawsweeper Bot added the rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. label Jun 13, 2026
@colton-octaria

Copy link
Copy Markdown
Contributor Author

Context: this is a companion to #90738 (inbound channel file reading for msteams) — same effort to bring the Microsoft Teams channel up to parity with the other group-capable channels. #90738 fixes a bug in how channel messages are read; this PR adds the keyword-mention seam (channels.msteams.mentionPatterns) so the agent can be addressed by name without an @, exactly as Discord/Slack/WhatsApp already allow.

On the "is this the best fix?" question: the alternatives are worse. Flipping requireMention=false would fire a model turn on every channel message (cost + unprompted replies); a Teams-specific keyword flag would invent a new concept. Reusing the existing messages.groupChat.mentionPatterns + the per-provider MentionPatternsPolicyConfig scoping policy (the established seam) keeps the gate's pre-model skip intact for non-matches and adds zero new concepts — msteams was simply the only group channel not wired into it.

The two PRs are independent (different files: #90738 touches the Graph attachment-fetch path; this touches the config schema + the mention gate), so they can land in any order.

@colton-octaria

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

The previous Codex run failed to execute (exit 1, and it reported it "could not read AGENTS.md completely") rather than evaluating the change, so the rating came back as off-meta tidepool. All GitHub CI checks are green — including Real behavior proof, build-artifacts, check-lint, check-prod-types, check-test-types, and the boundary/security/contract shards — so please retry the review against the actual diff.

@clawsweeper

clawsweeper Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

…tion

The MS Teams channel is the only group-capable channel that ignores
messages.groupChat.mentionPatterns: Discord, Slack and WhatsApp all expose a
per-provider mentionPatterns policy that scopes those patterns and treats a
text match as an implicit mention, but msteams drops every non-@mention group
message at the requireMention gate. This adds the same seam to msteams.

- config: channels.msteams.mentionPatterns (MentionPatternsPolicyConfig),
  matching the discord/slack/whatsapp shape; scopes the global
  messages.groupChat.mentionPatterns to selected Teams conversations.
- handler: build the mention regexes and OR a match into the wasMentioned fact
  fed to resolveInboundMentionDecision, so a named-without-@ message dispatches
  while non-matching messages still hit the requireMention skip (no extra model
  turn). Self-echo guarded so the bot does not trigger on its own posts (with
  RSC ChannelMessage.Read.Group the bot also receives its own channel messages).
- isMSTeamsKeywordMention helper (mirrors discord's mention-state resolution),
  unit-tested for match/case-insensitive/no-keyword/self-echo/DM/already-
  mentioned/empty-patterns/empty-text.
@colton-octaria
colton-octaria force-pushed the msteams-mention-patterns branch from e7707b6 to 763dd83 Compare June 13, 2026 04:01
@colton-octaria

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Rebased onto current main (was based on a 131-commit-behind fork base). The two prior Codex runs failed fast (exit 1, "AGENTS.md could not be read completely") while other PRs reviewed fine in the same window — the stale base looked like the likely cause of the review-context read failure. Head is now 763dd83 on top of 6cf06e8e. CI is green including Real behavior proof; please retry.

@clawsweeper

clawsweeper Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@colton-octaria

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Now rebased onto current main (head 763dd83 on 6cf06e8). The earlier post-rebase run was cancelled before completing. Please review against the current base.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 15, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: msteams Channel integration: msteams merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: S stale Marked as stale due to inactivity status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants