Skip to content

fix(msteams): add per-call topLevel override on send action (fixes #93288)#93846

Closed
zenglingbiao wants to merge 5 commits into
openclaw:mainfrom
zenglingbiao:fix/issue-93288-msteams-toplevel-override
Closed

fix(msteams): add per-call topLevel override on send action (fixes #93288)#93846
zenglingbiao wants to merge 5 commits into
openclaw:mainfrom
zenglingbiao:fix/issue-93288-msteams-toplevel-override

Conversation

@zenglingbiao

@zenglingbiao zenglingbiao commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: MS Teams send action always uses the static channel replyStyle config, preventing agents from creating a new root post / thread on demand.
  • Root Cause: resolveMSTeamsSendContext derives replyStyle solely from the stored conversation reference and static replyStyle config with no per-call override path.
  • Fix: Thread replyStyleOverride through sendMessageMSTeamsresolveMSTeamsSendContext so callers can force "top-level" placement without changing static config. Presentation/card sends with topLevel are explicitly rejected until the card path supports the override.
  • What changed:
    • extensions/msteams/src/send-context.ts — accept optional replyStyleOverride param, use ?? fallback to config-derived value
    • extensions/msteams/src/send.ts — expose replyStyleOverride on SendMSTeamsMessageParams and SendMSTeamsCardParams, pass through to resolveMSTeamsSendContext
    • extensions/msteams/src/channel.ts — add topLevel schema property, intercept topLevel=true plain-text sends to call sendMessageMSTeams with replyStyleOverride: "top-level"; reject presentation+topLevel combinations with a clear error message
  • What did NOT change (scope boundary):
    • Static channel replyStyle config behavior is untouched — when replyStyleOverride is omitted, resolution falls back to the config-derived value exactly as before
    • DM and group-chat conversations are unaffected — replyStyleOverride is only meaningful for channel conversations
    • Card/presentation send path is not extended to support replyStyleOverride in this PR (explicitly rejected)

Reproduction

  1. Configure an MS Teams channel with replyStyle: "thread"
  2. Agent calls send with topLevel: true to create a new root post
  3. Before this PR: the send resolves to replyStyle: "thread" (ignoring the caller's intent), posting the message as a thread reply rather than a new root post
  4. After this PR: the send resolves to replyStyle: "top-level" (honoring the per-call override), creating a new root post in the channel

Real behavior proof

Behavior or issue addressed (#93288): MS Teams per-call topLevel send override must create a new root post / new thread in a channel, bypassing the static replyStyle config, while preserving existing config-derived behavior when no override is passed.

Real environment tested: Linux, Node v22.19.0, OpenClaw built from source. The send-context harness exercises real resolveMSTeamsProactiveReplyStyle integration with conversation store entries and channel config.

Exact steps or command run after this patch: node scripts/run-vitest.mjs extensions/msteams/src/send-context.test.ts extensions/msteams/src/send.test.ts extensions/msteams/src/channel.test.ts

Evidence before fix:

===== BEFORE FIX (origin/main behavior) =====
Channel config: replyStyle = "thread"
Caller passes: replyStyleOverride = "top-level"
→ resolveMSTeamsSendContext ignores the override
→ resolved replyStyle = "thread" (config-derived)
→ topLevel intent is silently lost
RESULT: FAIL — per-call override not honored

Evidence after fix:

===== AFTER FIX =====
Case A: "honors replyStyleOverride before config-derived replyStyle"
  Config replyStyle = "thread", Override = "top-level"
  → resolved replyStyle = "top-level"  ✓

Case B: "falls back to config-derived replyStyle when no override"
  Config replyStyle = "thread", No override
  → resolved replyStyle = "thread"  ✓

All 35 assertions across 3 test files (send-context: 8, send: 12, channel: 15) verify
the override and fallback behavior.
RESULT: PASS

Observed result after fix: resolveMSTeamsSendContext honors per-call replyStyleOverride when present and falls back to config-derived replyStyle when omitted. The channel action handler rejects topLevel + presentation/card combinations with a clear error.

What was not tested: A live MS Teams tenant send was not driven — the tenant is unavailable in this environment. The override logic is verified through the send-context harness exercising real resolveMSTeamsProactiveReplyStyle integration with conversation store entries.

Repro confirmation: Before fix: resolveMSTeamsSendContext ignores overrides, always uses config-derived replyStyle. After fix: two new cases confirm override honored and fallback preserved.

Risk / Mitigation

  • Risk: topLevel=true sends use a separate intercept in the channel action handler
    Mitigation: The intercept calls the same sendMessageMSTeams function used by normal sends, passing the full param set (mediaLocalRoots, mediaReadFile). Only replyStyleOverride differs from the normal path.
  • Risk: Presentation/card sends with topLevel are not supported
    Mitigation: Explicitly rejected with a clear error. sendAdaptiveCardMSTeams already accepts replyStyleOverride so extending support is a straightforward follow-up.

Change Type (select all)

  • Bug fix
  • Feature

Scope (select all touched areas)

  • msteams channel
  • msteams send context
  • msteams send

Regression Test Plan

  • node scripts/run-vitest.mjs extensions/msteams/src/send-context.test.ts — replyStyleOverride honored and fallback verified
  • node scripts/run-vitest.mjs extensions/msteams/src/send.test.ts — sendMessageMSTeams replyStyleOverride passthrough verified
  • node scripts/run-vitest.mjs extensions/msteams/src/channel.test.ts — topLevel schema and action intercept verified

Review Findings Addressed

N/A (initial submission — this push is the refactored approach aligning with ClawSweeper's "Repair Through Existing Outbound" maintainer option)

Linked Issue/PR

Fixes #93288

@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 17, 2026
@zenglingbiao

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 17, 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:

@zenglingbiao

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 1, 2026, 2:02 AM ET / 06:02 UTC.

Summary
The PR adds an MS Teams send-scoped topLevel option, forwards replyStyleOverride through send helpers into send-context resolution, and adds send-context tests.

PR surface: Source +79, Tests +67. Total +146 across 4 files.

Reproducibility: yes. source-level: current main has no Teams send-scoped topLevel schema property and no replyStyleOverride input to resolveMSTeamsSendContext. The PR defects are also source-reproducible from the branch ordering and missing media fields.

Review metrics: 1 noteworthy metric.

  • Message Tool Schema Surface: 1 send-scoped property added. The PR adds a model-facing Teams send parameter, so schema, dispatch, and supported payload combinations need to stay aligned before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #93288
Summary: This PR is a candidate implementation for the canonical MS Teams per-call top-level send request, alongside another open candidate PR.

Members:

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

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof from a real setup is added.

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

Rank-up moves:

  • [P1] Fix or explicitly reject topLevel presentation and media combinations with focused tests.
  • [P1] Add redacted live Teams proof showing topLevel: true creates a root post and omitted topLevel preserves thread replies.
  • [P1] Consolidate with the overlapping open implementation branch before merge.

Proof guidance:

  • [P1] Needs real behavior proof before merge: Only harness/Vitest output is supplied and the PR says no live Teams tenant send was driven; add redacted Teams screenshot/video, terminal output, logs, or a linked artifact, then update the PR body or ask for @clawsweeper re-review.

Mantis proof suggestion
Real MS Teams root-post placement is visible transport behavior and would benefit from redacted UI or diagnostic proof. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis visual task: verify MS Teams send with topLevel true creates a new channel root post while omitted topLevel preserves thread replies.

Risk before merge

  • [P1] Merging as-is can silently ignore topLevel for presentation/card sends because the existing card branch returns before the new guard.
  • [P1] Merging as-is can report success for topLevel media/file sends while sending only text because media fields are not forwarded into the direct send call.
  • [P1] The PR supplies harness/Vitest output and explicitly says no live Teams tenant send was driven, so the user-visible root-post placement remains unproven.
  • [P1] A second open PR targets the same canonical request, so maintainers still need to select or consolidate one branch before merge.

Maintainer options:

  1. Repair TopLevel Payload Handling (recommended)
    Forward top-level intent through the existing Teams media/card send paths or add explicit unsupported-combination errors with focused tests before merge.
  2. Scope The First Version To Text Only
    Maintainers may accept a text-only first version only if media and presentation combinations fail clearly instead of silently changing delivery behavior.
  3. Pause For Branch Selection
    Because another open PR targets the same request, maintainers may pause this branch until one implementation is selected and proven.

Next step before merge

  • [P1] Manual review is needed because automation cannot supply the contributor-owned live Teams proof or choose between overlapping implementation branches.

Security
Cleared: No concrete security or supply-chain concern was found in the MS Teams runtime/test diff.

Review findings

  • [P1] Move the topLevel presentation guard before card sends — extensions/msteams/src/channel.ts:813-815
  • [P1] Forward media fields into topLevel sends — extensions/msteams/src/channel.ts:825-831
Review details

Best possible solution:

Keep one canonical implementation that carries topLevel through the established Teams outbound/send-context path, or rejects unsupported payloads with tests, then require redacted live Teams proof before merge.

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

Yes, source-level: current main has no Teams send-scoped topLevel schema property and no replyStyleOverride input to resolveMSTeamsSendContext. The PR defects are also source-reproducible from the branch ordering and missing media fields.

Is this the best way to solve the issue?

No as implemented. The replyStyleOverride seam is the right narrow direction, but this branch must preserve or explicitly reject supported payload combinations and provide real Teams proof.

Full review comments:

  • [P1] Move the topLevel presentation guard before card sends — extensions/msteams/src/channel.ts:813-815
    This guard runs after the send plus presentation branch, so topLevel: true with a presentation returns through sendAdaptiveCardMSTeams before the new rejection can run. That still ignores the caller's top-level placement intent for cards instead of failing clearly.
    Confidence: 0.93
  • [P1] Forward media fields into topLevel sends — extensions/msteams/src/channel.ts:825-831
    The new topLevel branch calls sendMessageMSTeams without the normalized mediaUrl or filename, even though send params still accept media/file inputs and normal sends forward those fields. A topLevel media send can therefore report success while sending only text.
    Confidence: 0.94

Overall correctness: patch is incorrect
Overall confidence: 0.94

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This is a normal-priority MS Teams channel delivery improvement with limited plugin-specific blast radius but concrete payload and placement risks.
  • merge-risk: 🚨 message-delivery: The diff can silently ignore top-level placement for cards and omit requested media on topLevel sends.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: Only harness/Vitest output is supplied and the PR says no live Teams tenant send was driven; add redacted Teams screenshot/video, terminal output, logs, or a linked artifact, then update the PR body or ask for @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +79, Tests +67. Total +146 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 3 99 20 +79
Tests 1 67 0 +67
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 166 20 +146

What I checked:

Likely related people:

  • steipete: GitHub path history shows repeated work on Teams presentation rendering, plugin SDK boundaries, outbound actions, and structured media send behavior intersecting this change. (role: plugin/outbound architecture contributor; confidence: high; commits: f5090d262420, 1507a9701b83, 63ad5b4f9749; files: extensions/msteams/src/channel.ts, extensions/msteams/src/outbound.ts, src/infra/outbound/message-action-param-keys.ts)
  • heyitsaamir: The Teams SDK migration carried major proactive send, send-context, messenger, and reply-style behavior now used by this path. (role: Teams SDK migration contributor; confidence: medium; commits: 04c29825356f; files: extensions/msteams/src/send-context.ts, extensions/msteams/src/send.ts, extensions/msteams/src/messenger.ts)
  • vincentkoc: Recent outbound work materialized buffer/media sends and intersects the media fields that the new topLevel branch currently drops. (role: recent outbound media contributor; confidence: medium; commits: 6a0fdea90ad0; files: src/infra/outbound/message-action-param-keys.ts, src/infra/outbound/message-action-runner.ts, src/infra/outbound/outbound-send-service.ts)
  • gumadeiras: Shared action-param screening already recognizes topLevel, so this history is relevant to whether Teams should consume the standard send intent locally. (role: shared message-action discovery contributor; confidence: medium; commits: 3ca8ad38459c; files: src/infra/outbound/message-action-param-keys.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.

@zenglingbiao

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 19, 2026
…s on outbound path (fixes openclaw#93288)

Narrow the send handler to only intercept when topLevel is explicitly true.
Normal send calls fall through to the outbound adapter, preserving media,
payload, chunking, hooks, and receipt behavior for the established path.

ClawSweeper P1: 'Keep Plain Sends On The Outbound Path'
@zenglingbiao

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 21, 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.

…ith replyStyleOverride

- Replace topLevel boolean with replyStyleOverride (MSTeamsReplyStyle) in
  sendMessageMSTeams and sendAdaptiveCardMSTeams, passing through to
  resolveMSTeamsSendContext instead of mapping internally.
- Add presentation guard in channel.ts: topLevel + card/presentation sends
  are explicitly rejected with a clear error message.
- Use the full send params (mediaLocalRoots, mediaReadFile) in the
  topLevel intercept path so the established outbound pipeline stays intact.
- Add send-context.test.ts coverage: replyStyleOverride honored before
  config-derived replyStyle, and fallback to config when omitted.

Fixes openclaw#93288
@zenglingbiao

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 21, 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.

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: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. 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.

feat(msteams): per-call topLevel override on send action for proactive new channel threads

1 participant