Skip to content

feat(msteams): implement sendPayload for interactive approval cards#66327

Open
johnturek wants to merge 4 commits intoopenclaw:mainfrom
johnturek:feat/msteams-sendpayload-approval-cards
Open

feat(msteams): implement sendPayload for interactive approval cards#66327
johnturek wants to merge 4 commits intoopenclaw:mainfrom
johnturek:feat/msteams-sendpayload-approval-cards

Conversation

@johnturek
Copy link
Copy Markdown

Summary

Implements sendPayload for the MS Teams channel extension so approval prompts render as Adaptive Cards with clickable buttons instead of plain text with raw /approve commands.

Closes #64690

Problem

The Teams outbound adapter only implemented sendText, sendMedia, and sendPoll. The delivery pipeline checks handler.sendPayload && hasReplyPayloadContent(...) before dispatching interactive payloads — since Teams had no sendPayload, all approval prompts fell through to sendText, rendering as:

Reply /approve abc123 yes to approve

Solution

Added sendPayload to the Teams outbound adapter following the patterns established by Slack, Discord, and Telegram:

  1. extensions/msteams/src/outbound.ts:

    • Added buildApprovalAdaptiveCard() helper that maps interactive button blocks to Adaptive Card Action.Submit buttons
    • Uses messageBack (not imBack) to avoid echoing raw approval tokens — matches the existing poll card pattern in polls.ts
    • Honors pre-built channelData.msteams.card if supplied
    • Uses resolveInteractiveTextFallback() to derive card body text from text blocks
    • Sends media first via sendPayloadMediaSequenceAndFinalize, then finalizes with the card
    • Falls back to sendTextMediaPayload when no interactive buttons are present
  2. extensions/msteams/src/channel.ts:

    • Wired sendPayload directly on the outbound config (since createRuntimeOutboundDelegates only supports sendText/sendMedia/sendPoll), following the Slack channel pattern

Result: Approval prompts render as Adaptive Cards with "Approve" / "Deny" buttons. Clicking a button sends the /approve command via messageBack.

Testing

  • 3 new test cases in outbound.test.ts:
    • Interactive buttons → Adaptive Card with messageBack actions
    • No buttons → falls back to sendText
    • Pre-built channelData card → sent directly
  • All 66 Teams extension test files pass (860 tests)
  • Run: pnpm test:extension msteams

AI Disclosure

  • AI-assisted (GitHub Copilot CLI)
  • Fully tested — pnpm test:extension msteams passes (860/860 tests)
  • I understand what the code does
  • I work at Microsoft and can verify this against a real Teams tenant

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>
@openclaw-barnacle openclaw-barnacle Bot added channel: msteams Channel integration: msteams size: M labels Apr 14, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 14, 2026

Greptile Summary

Adds sendPayload to the MS Teams outbound adapter so approval prompts render as Adaptive Cards with messageBack buttons instead of plain text. The implementation follows the Slack/Discord sendPayloadMediaSequenceAndFinalize pattern and correctly wires sendPayload on the channel outbound config. Two style-level issues are worth addressing before merge: the button style mapping drops the "positive" style for "primary" and "success" buttons, and the msteams placement inside data is inconsistent with the established polls.ts pattern (though both are valid Teams formats).

Confidence Score: 5/5

  • Safe to merge — no functional bugs found; remaining findings are style and UX polish.
  • All findings are P2: the button style mapping omission is a visual/UX gap (buttons still click correctly), and the msteams placement inconsistency with polls.ts is a style concern (Teams supports both formats). No data loss, auth, or routing regressions were identified.
  • extensions/msteams/src/outbound.ts — button style mapping and msteams placement consistency.
Prompt To Fix All With AI
This 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

Comment thread extensions/msteams/src/outbound.ts Outdated
Comment thread extensions/msteams/src/outbound.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread extensions/msteams/src/outbound.ts Outdated
Comment thread extensions/msteams/src/outbound.ts
JT Turek (Federal) and others added 3 commits April 14, 2026 06:45
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>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +136 to +138
send: async ({ text, mediaUrl }) =>
await sendMessageMSTeams({
cfg: ctx.cfg,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 27, 2026

Codex review: needs changes before merge.

What this changes:

The branch adds MS Teams outbound sendPayload support for interactive approval Adaptive Cards, wires it through the Teams channel outbound adapter, and adds unit coverage for button, fallback, and prebuilt-card paths.

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:

  • [P2] Route card media sends through outbound deps — extensions/msteams/src/outbound.ts:137
  • [P3] Add the required changelog entry — extensions/msteams/src/outbound.ts:115
Review details

Best possible solution:

Land a rebased Teams-plugin-owned sendPayload implementation that renders approval buttons as Adaptive Cards, preserves media through the same dep-aware send path as sendMedia, adds focused coverage and the required changelog entry, and leaves the core approval pipeline unchanged.

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 deliver.ts: Teams cannot receive native payload routing because its handler has no sendPayload. The PR regression is reproducible with a focused sendPayload call that includes payload.mediaUrls and deps.msteams, then observes the direct sendMessageMSTeams path instead of the injected sender.

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:

  • [P2] Route card media sends through outbound deps — extensions/msteams/src/outbound.ts:137
    The new card-media branches call sendMessageMSTeams directly instead of resolving ctx.deps.msteams like existing sendText and sendMedia. In injected or isolated delivery paths, interactive/prebuilt payloads with media can ignore the provided sender and attempt the real Teams transport; resolve the sender through the same dep-aware path before sending media.
    Confidence: 0.88
  • [P3] Add the required changelog entry — extensions/msteams/src/outbound.ts:115
    This is a user-facing Teams feature, but the branch does not update CHANGELOG.md. OpenClaw policy requires user-facing feat changes to add a single-line changelog entry with allowed contributor credit before merge.
    Confidence: 0.84

Overall correctness: patch is incorrect
Overall confidence: 0.86

Acceptance criteria:

  • pnpm test extensions/msteams/src/outbound.test.ts
  • pnpm exec oxfmt --check --threads=1 extensions/msteams/src/outbound.ts extensions/msteams/src/outbound.test.ts extensions/msteams/src/channel.ts CHANGELOG.md
  • pnpm check:changed

What I checked:

Likely related people:

  • steipete: Recent commits on Teams outbound, outbound dependency resolution, shared reply-payload helpers, and core payload delivery make this the strongest routing candidate for the dependency seam the PR needs to respect. (role: recent maintainer and outbound seam owner; confidence: high; commits: 8a731c1ef77b, 3bb02d333865, 8d73bc77fa5d; files: extensions/msteams/src/outbound.ts, src/plugin-sdk/reply-payload.ts, src/infra/outbound/send-deps.ts)
  • sudie-codes: Recent merged Teams work covers send helper behavior, file consent, media upload/pending-upload handling, and channel actions adjacent to the outbound media path this PR touches. (role: Teams send/media feature maintainer; confidence: medium; commits: ba1b8424f48e, 784318799bf0, 6e970010f77b; files: extensions/msteams/src/send.ts, extensions/msteams/src/monitor-handler.ts, extensions/msteams/src/channel.ts)
  • BradGroux: Introduced the current Teams Adaptive Card Action.Submit invoke handling, which is adjacent to the button-click behavior this outbound card feature relies on. (role: adjacent Teams Adaptive Card action owner; confidence: medium; commits: 06c6ff6670cd; files: extensions/msteams/src/monitor-handler.ts, extensions/msteams/src/monitor-handler.adaptive-card.test.ts)

Remaining risk / open question:

  • GitHub currently reports the branch as mergeable: false, so a rebase or replacement branch is needed before final CI validation.
  • No tests were run during this read-only review; the listed targeted and changed-gate checks still need to run after the repair.

Codex review notes: model gpt-5.5, reasoning high; reviewed against a7a8c8121a67.

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

Labels

channel: msteams Channel integration: msteams size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Teams channel: implement sendPayload for interactive approval cards

1 participant