feat(msteams): implement sendPayload for interactive approval cards#66327
feat(msteams): implement sendPayload for interactive approval cards#66327johnturek wants to merge 4 commits intoopenclaw:mainfrom
Conversation
Add sendPayload to the MS Teams outbound adapter so approval prompts render as Adaptive Cards with clickable buttons instead of plain text. Implementation: - Map interactive button blocks to Adaptive Card Action.Submit buttons using messageBack (hidden value + friendly displayText), matching the existing poll card pattern - Honor pre-built channelData.msteams.card if supplied - Send media first via sendPayloadMediaSequenceAndFinalize, then finalize with the Adaptive Card - Fall back to sendText when no interactive buttons are present - Resolve text via resolveInteractiveTextFallback for text-only blocks - Wire sendPayload in channel.ts alongside createRuntimeOutboundDelegates Closes openclaw#64690 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Greptile SummaryAdds Confidence Score: 5/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/msteams/src/outbound.ts
Line: 38
Comment:
**"primary" and "success" styles silently fall through to "default"**
`InteractiveButtonStyle` includes `"primary"`, `"success"`, `"secondary"`, and `"danger"`. Teams Adaptive Cards support `style: "positive"` for `Action.Submit`, which renders as a blue/green button — exactly what the "Approve" button in an approval workflow should look like. With the current mapping, a `style: "primary"` button renders grey (same as unstyled), while the "Deny" button correctly renders red. The Discord adapter shows the intended mapping: `"primary"` → positive/primary, `"success"` → positive/success.
```suggestion
style: b.style === "danger" ? "destructive" : b.style === "primary" || b.style === "success" ? "positive" : "default",
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/msteams/src/outbound.ts
Line: 39-46
Comment:
**`msteams` placement diverges from the polls pattern the PR claims to follow**
The PR description says this "matches the existing poll card pattern in `polls.ts`", but the structures differ. In `polls.ts` the `msteams` block is a direct sibling of `data` on the action object (action-level), while here it is nested inside `data`. The Teams Adaptive Card spec supports both forms, but keeping one consistent convention inside this codebase avoids confusion. `welcome-card.ts` also uses the `data.msteams` form (for `imBack`), so adopting that as the canonical pattern is reasonable — just update the PR description to reflect this accurately, or restructure to align with `polls.ts`:
```ts
// polls.ts / action-level pattern
{
type: "Action.Submit",
title: b.label,
style: ...,
data: { openclawApproval: b.value },
msteams: {
type: "messageBack",
text: b.value,
displayText: b.label,
value: { openclawApproval: b.value },
},
}
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "feat(msteams): implement sendPayload for..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 989abc08fc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 42c6fcff27
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| send: async ({ text, mediaUrl }) => | ||
| await sendMessageMSTeams({ | ||
| cfg: ctx.cfg, |
There was a problem hiding this comment.
Route payload media sends through outbound deps
sendPayload sends media by calling sendMessageMSTeams directly, which bypasses the ctx.deps indirection that sendText/sendMedia already honor via resolveOutboundSendDep. In environments that inject deps.msteams (tests, alternate transports, isolated delivery), interactive payloads and prebuilt-card payloads with media will ignore the injected sender and attempt the real Teams transport instead, causing inconsistent behavior or failures; this path should resolve/send through the same dep-aware sender as the other outbound methods.
Useful? React with 👍 / 👎.
|
Codex review: needs changes before merge. What this changes: The branch adds MS Teams outbound Required change before merge: This is a narrow repair candidate: the PR is useful, maintainer-modifiable, and blocked by a concrete dep-routing bug, a missing changelog entry, and stale merge state rather than product judgment. Security review: Security review cleared: Security review cleared: the diff changes Teams outbound plugin logic and tests only, with no workflow, dependency, package resolution, install script, secrets, permission, or privileged execution changes. Review findings:
Review detailsBest possible solution: Land a rebased Teams-plugin-owned Do we have a high-confidence way to reproduce the issue? Yes. The current-main gap is reproducible by tracing an interactive approval payload through Is this the best way to solve the issue? No. The PR direction is the right narrow owner-side fix, but the current implementation should route card-media sends through the existing outbound dependency seam, add the changelog entry, and be rebased before merge. Full review comments:
Overall correctness: patch is incorrect Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against a7a8c8121a67. |
Summary
Implements
sendPayloadfor the MS Teams channel extension so approval prompts render as Adaptive Cards with clickable buttons instead of plain text with raw/approvecommands.Closes #64690
Problem
The Teams outbound adapter only implemented
sendText,sendMedia, andsendPoll. The delivery pipeline checkshandler.sendPayload && hasReplyPayloadContent(...)before dispatching interactive payloads — since Teams had nosendPayload, all approval prompts fell through tosendText, rendering as:Solution
Added
sendPayloadto the Teams outbound adapter following the patterns established by Slack, Discord, and Telegram:extensions/msteams/src/outbound.ts:buildApprovalAdaptiveCard()helper that maps interactive button blocks to Adaptive CardAction.SubmitbuttonsmessageBack(notimBack) to avoid echoing raw approval tokens — matches the existing poll card pattern inpolls.tschannelData.msteams.cardif suppliedresolveInteractiveTextFallback()to derive card body text from text blockssendPayloadMediaSequenceAndFinalize, then finalizes with the cardsendTextMediaPayloadwhen no interactive buttons are presentextensions/msteams/src/channel.ts:sendPayloaddirectly on the outbound config (sincecreateRuntimeOutboundDelegatesonly supports sendText/sendMedia/sendPoll), following the Slack channel patternResult: Approval prompts render as Adaptive Cards with "Approve" / "Deny" buttons. Clicking a button sends the
/approvecommand via messageBack.Testing
outbound.test.ts:pnpm test:extension msteamsAI Disclosure
pnpm test:extension msteamspasses (860/860 tests)