Skip to content

fix(msteams): delete FileConsentCard after user accepts, declines, or upload expires#57364

Open
HangGlidersRule wants to merge 1 commit intoopenclaw:mainfrom
HangGlidersRule:fix/msteams-file-consent-card-cleanup
Open

fix(msteams): delete FileConsentCard after user accepts, declines, or upload expires#57364
HangGlidersRule wants to merge 1 commit intoopenclaw:mainfrom
HangGlidersRule:fix/msteams-file-consent-card-cleanup

Conversation

@HangGlidersRule
Copy link
Copy Markdown
Contributor

Problem

The FileConsentCard (Allow/Decline prompt for file uploads) remains in the Teams chat after the user acts on it, cluttering the conversation with stale interactive cards. Users see orphaned Allow/Decline buttons even after files have been successfully uploaded or the upload has expired.

Solution

Call context.deleteActivity(activity.replyToId) to remove the original consent card after processing. The invoke activity from Bot Framework includes the consent card's activity ID in replyToId.

Deletion is applied on all four code paths in handleFileConsentInvoke:

Path Before After
User accepts, upload succeeds Consent card remains Deleted, only FileInfoCard shown
User declines Consent card remains Deleted
Pending upload expired (not found) Consent card remains, expired message sent Deleted, expired message sent
Conversation mismatch (security) Consent card remains Deleted

Implemented as a best-effort tryDeleteConsentCard() helper — if deletion fails (e.g. permissions, timing), the upload flow continues normally with a debug log.

Per Microsoft docs:

Optionally, remove the original consent card if you do not want the user to accept further uploads of the same file.

Testing

  • Added 4 new tests covering consent card deletion on all paths
  • Updated existing test harness to include deleteActivity mock and replyToId field
  • Verified in production Teams environment (NemoClaw deployment)

AI Disclosure

This PR was developed with AI assistance (Clawdbot on OpenClaw). The bug was identified during real-world testing of file uploads in a Teams bot deployment.

@openclaw-barnacle openclaw-barnacle Bot added channel: msteams Channel integration: msteams size: S labels Mar 30, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 30, 2026

Greptile Summary

This PR fixes a UX issue in the MS Teams integration where FileConsentCard messages (Allow/Decline prompts for file uploads) persisted in the chat after the user acted on them. The fix introduces a tryDeleteConsentCard() helper inside handleFileConsentInvoke() that calls context.deleteActivity(activity.replyToId) in a try-catch so that deletion failures are non-fatal.

  • Four cleanup paths are covered: upload success, user decline, expired pending upload, and conversation-mismatch (both accept and decline variants).
  • New tests verify each path and confirm resilience when deleteActivity rejects.
  • The implementation correctly follows the Microsoft documentation guidance on optionally removing the consent card after processing.

One gap to address: The catch block for a failed uploadToConsentUrl (lines 196-199 of monitor-handler.ts) does not call tryDeleteConsentCard. Since removePendingUpload runs in finally regardless, a failed upload leaves an orphaned card with active buttons — clicking it again will just show the "expired" message. Adding tryDeleteConsentCard(\"upload-failed\") in the catch branch would make the cleanup consistent with all other paths.

Confidence Score: 4/5

Safe to merge; the upload-failure gap is a UX inconsistency and the one code path not addressed by this PR.

The core implementation is correct, well-tested, and follows MS documentation. The only issue is the upload-failure catch branch not deleting the consent card — all other paths clean up correctly. This is a P2 gap that leaves stale interactive cards in one edge case but does not break the main upload flow or introduce data loss.

extensions/msteams/src/monitor-handler.ts — the catch block at lines 196-199 inside handleFileConsentInvoke.

Important Files Changed

Filename Overview
extensions/msteams/src/monitor-handler.ts Adds a nested tryDeleteConsentCard() best-effort helper and calls it on four of the five consent-processing code paths (success, decline, expiry, conversation-mismatch). The upload-failure catch branch is the only path that still leaves the consent card visible.
extensions/msteams/src/monitor-handler.file-consent.test.ts Adds deleteActivity mock to the shared test context builder and four new tests covering each cleanup path. Coverage is solid; the 'does not throw' resilience test correctly verifies best-effort behaviour.

Comments Outside Diff (1)

  1. extensions/msteams/src/monitor-handler.ts, line 196-199 (link)

    P2 Orphaned consent card on upload failure

    When uploadToConsentUrl throws, the catch block sends an error message to the user but never calls tryDeleteConsentCard. At the same time, removePendingUpload(uploadId) runs in finally, so the pending upload is gone. This leaves the original consent card visible in the chat with active Allow/Decline buttons — clicking "Accept" again will just produce the "expired" message, which is confusing UX.

    All other paths in this PR now clean up the card. Consider adding deletion in the catch branch as well:

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: extensions/msteams/src/monitor-handler.ts
    Line: 196-199
    
    Comment:
    **Orphaned consent card on upload failure**
    
    When `uploadToConsentUrl` throws, the `catch` block sends an error message to the user but never calls `tryDeleteConsentCard`. At the same time, `removePendingUpload(uploadId)` runs in `finally`, so the pending upload is gone. This leaves the original consent card visible in the chat with active Allow/Decline buttons — clicking "Accept" again will just produce the "expired" message, which is confusing UX.
    
    All other paths in this PR now clean up the card. Consider adding deletion in the `catch` branch as well:
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/msteams/src/monitor-handler.ts
Line: 196-199

Comment:
**Orphaned consent card on upload failure**

When `uploadToConsentUrl` throws, the `catch` block sends an error message to the user but never calls `tryDeleteConsentCard`. At the same time, `removePendingUpload(uploadId)` runs in `finally`, so the pending upload is gone. This leaves the original consent card visible in the chat with active Allow/Decline buttons — clicking "Accept" again will just produce the "expired" message, which is confusing UX.

All other paths in this PR now clean up the card. Consider adding deletion in the `catch` branch as well:

```suggestion
      } catch (err) {
        log.debug?.("file upload failed", { uploadId, error: String(err) });
        await tryDeleteConsentCard("upload-failed");
        await context.sendActivity(`File upload failed: ${String(err)}`);
      } finally {
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(msteams): delete FileConsentCard aft..." | Re-trigger Greptile

@HangGlidersRule HangGlidersRule force-pushed the fix/msteams-file-consent-card-cleanup branch from 8188114 to 2e90523 Compare March 30, 2026 00:49
@HangGlidersRule
Copy link
Copy Markdown
Contributor Author

Addressed — added tryDeleteConsentCard("upload-failed") in the catch branch and a test covering the upload-failure path. All five code paths now clean up the consent card. Force-pushed.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 28, 2026

Codex review: needs real behavior proof before merge.

Summary
The PR adds best-effort deletion of stale MS Teams FileConsentCard prompts in the active file-consent invoke handler, extends focused tests, and adds a changelog entry.

Reproducibility: yes. source inspection gives a high-confidence reproduction path: current main routes Teams file-consent invokes into handleMSTeamsFileConsentInvoke, and several terminal paths lack deleteActivity. I did not run a live Teams tenant scenario.

Real behavior proof
Needs real behavior proof before merge: The PR body claims production Teams verification and a maintainer comment lists local checks, but there is no inspectable screenshot, recording, terminal output, copied live output, linked artifact, or redacted runtime log showing the after-fix Teams behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, ask a maintainer to comment @clawsweeper re-review.

Next step before merge
The remaining blocker is contributor or maintainer proof from a real Teams setup, not a narrow code defect that ClawSweeper can repair automatically.

Security
Cleared: Cleared: the diff touches only the Teams handler, focused tests, and changelog, uses the existing scoped deleteActivity API, and introduces no dependency, workflow, secret, or package-resolution changes.

Review details

Best possible solution:

Land the active-handler cleanup after the contributor or maintainer adds inspectable after-fix Teams proof, while preserving the successful-accept FileInfoCard replacement behavior.

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

Yes, source inspection gives a high-confidence reproduction path: current main routes Teams file-consent invokes into handleMSTeamsFileConsentInvoke, and several terminal paths lack deleteActivity. I did not run a live Teams tenant scenario.

Is this the best way to solve the issue?

Yes for the code direction: the rebased patch moves cleanup into the active handler, uses the stored consent-card id or replyToId as appropriate, and adds focused coverage. It is not merge-ready until inspectable real behavior proof is added.

What I checked:

  • Active route on current main: Current main routes fileConsent/invoke activities through respondToMSTeamsFileConsentInvoke, so the active behavior is in file-consent-invoke.ts. (extensions/msteams/src/monitor-handler.ts:334, b0966f535644)
  • Source-reproducible stale-card paths: Current main's active handler handles mismatch, upload failure, expired upload, and decline paths without deleting the original card; success only replaces the stored card when a stored activity id exists. (extensions/msteams/src/file-consent-invoke.ts:48, b0966f535644)
  • PR patch target: The prepared head adds replyToActivityId, tryDeleteConsentCard, and cleanup calls in extensions/msteams/src/file-consent-invoke.ts for mismatch, success fallback, upload failure, expired upload, and decline paths. (extensions/msteams/src/file-consent-invoke.ts:45, 5e0b7a948f67)
  • Focused coverage added: The PR extends the file-consent test harness with deleteActivity/replyToId and adds tests for success without stored id, update fallback, upload failure, expired upload, mismatch, decline, and delete-failure resilience. (extensions/msteams/src/monitor-handler.file-consent.test.ts:66, 5e0b7a948f67)
  • Dependency contract: Microsoft Teams file bot docs state that removing the original consent card after processing is an optional supported behavior.
  • Proof gate: The latest head has CI mostly green, but the Real behavior proof check is the sole failure; no screenshot, recording, terminal output, runtime log, or linked artifact is present in the PR body or comments. (5e0b7a948f67)

Likely related people:

  • sudie-codes: Authored the commit that introduced the Teams file-consent callback flow, pending upload handling, and focused tests that this PR builds on. (role: introduced behavior; confidence: high; commits: 784318799bf0; files: extensions/msteams/src/monitor-handler.ts, extensions/msteams/src/monitor-handler.file-consent.test.ts, extensions/msteams/src/pending-uploads.ts)
  • steipete: Recent file-consent hardening and follow-up landing work touched the conversation binding and timeout behavior around this handler/test surface. (role: recent maintainer; confidence: medium; commits: 347f7b955006, 2e97d0dd95b3; files: extensions/msteams/src/monitor-handler.ts, extensions/msteams/src/monitor-handler.file-consent.test.ts, CHANGELOG.md)
  • Takhoffman: Recently maintained the Teams send helper path where consent-card activity ids are stored for later replacement or cleanup. (role: adjacent owner; confidence: medium; commits: 4ec51f2d5f2c; files: extensions/msteams/src/send.ts, extensions/msteams/src/send.test.ts)

Remaining risk / open question:

  • Live Teams deletion behavior remains unproven by an inspectable artifact; the PR's Real behavior proof check is failing.

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

… upload expires

The FileConsentCard (Allow/Decline prompt) previously remained in the chat
after the user acted on it, cluttering the conversation with stale cards.

Now the consent card message is deleted via context.deleteActivity() on all
four paths:
- User accepts and upload succeeds
- User declines
- Pending upload expired (not found in memory)
- Conversation ID mismatch (cross-conversation replay protection)

Deletion is best-effort with a try/catch — if it fails, the upload flow
continues normally.

Per Microsoft docs: 'Optionally, remove the original consent card if you do
not want the user to accept further uploads of the same file.'
https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4

Co-authored-by: Clawdbot <clawdbot@openclaw.ai>
@BradGroux BradGroux force-pushed the fix/msteams-file-consent-card-cleanup branch from 2e90523 to 5e0b7a9 Compare May 8, 2026 09:18
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 8, 2026
@BradGroux
Copy link
Copy Markdown
Member

Maintainer prep update for #57364.

What changed:

  • Rebased the PR onto current origin/main.
  • Moved the FileConsentCard cleanup from the stale inlined monitor-handler.ts flow into the active extensions/msteams/src/file-consent-invoke.ts handler.
  • Preserved the successful accept path that replaces the consent card with a FileInfoCard via updateActivity.
  • Added cleanup coverage for success without stored card id, update fallback, upload failure, expired upload, conversation mismatch, decline, and delete-failure resilience.
  • Added the required CHANGELOG.md entry with thanks to @HangGlidersRule.

Prepared head:

  • Before: 2e90523c2c9ce0df128c3f95fb0cd809d2b00b17
  • After: 5e0b7a948f674cc2f03d024f374825f80d9155ef

Verification run locally after the final rebase:

  • pnpm test -- extensions/msteams/src/monitor-handler.file-consent.test.ts passed: 14 tests.
  • pnpm build passed.
  • pnpm check passed.

Fresh CI is now running on the prepared head.

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: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants