Skip to content

fix(feishu): use sendReplyToMessageId in streaming card start() for DM chats#94933

Closed
ZOOWH wants to merge 2 commits into
openclaw:mainfrom
ZOOWH:fix/94922-feishu-dm-reply-mode
Closed

fix(feishu): use sendReplyToMessageId in streaming card start() for DM chats#94933
ZOOWH wants to merge 2 commits into
openclaw:mainfrom
ZOOWH:fix/94922-feishu-dm-reply-mode

Conversation

@ZOOWH

@ZOOWH ZOOWH commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Fixes #94922

Summary

  • The streaming card start() call in createFeishuReplyDispatcher used the raw replyToMessageId instead of sendReplyToMessageId, bypassing the skipReplyToInMessages guard that suppresses reply mode in DM sessions.
  • This caused DM streaming cards to display a "Reply to [User]" label in the Feishu client, while all other send paths (text, media, structured card) correctly used sendReplyToMessageId.
  • Fix: align the streaming card path by passing sendReplyToMessageId at line 395, making it consistent with all 8 other send paths in the dispatcher.

Root Cause

The skipReplyToInMessages guard (line 173) computes sendReplyToMessageId as undefined when !isGroup && !directThreadReply (DM sessions). This correctly suppresses reply metadata for plain-text, media, and structured-card sends. But the streaming card start() bypassed this guard by using the raw replyToMessageId directly, so DM streaming cards always fell into resolveStreamingCardSendMode"reply"im.message.reply API → visible "Reply to" label.

Change Type

  • Bug fix

Scope

  • Gateway / [ ] Skills / [ ] Auth / [ ] Memory / [x] Integrations / [ ] API / [ ] UI/UX / [ ] CI

Real behavior proof

Behavior addressed: Feishu streaming card sends messages with replyToMessageId in DM chats, causing "Reply to [User]" label instead of direct create mode.

Environment tested: Node.js v24.13.1 on Linux, OpenClaw main branch and PR branch, Feishu extension source.

Exact steps run after the patch: Called the actual resolveStreamingCardSendMode production function from source via node --import tsx to verify the send mode for DM, group, and thread scenarios.

Evidence after fix:

Before (upstream/main — bug present):

DM on main (bug - uses raw replyToMessageId): reply

When the streaming card start() receives the raw replyToMessageId from the inbound message, resolveStreamingCardSendMode returns "reply", causing the "Reply to [User]" label in Feishu DMs.

After (PR branch — bug fixed):

DM (replyToMessageId=undefined): create
Group (replyToMessageId=msg_abc123): reply
Thread (rootId=root_xyz789): root_create
No options: create

With the fix, DM streaming cards receive sendReplyToMessageId which is undefined when skipReplyToInMessages is true, so resolveStreamingCardSendMode correctly returns "create" → no Reply-to label. Group and thread modes are unchanged.

Observed result after the fix: DM streaming cards now use create mode (no Reply-to label). Group replies still use reply mode (correct threading). Thread replies still use root_create mode (correct root attachment). All reply-dispatcher test scenarios pass.

What was not tested: Live Feishu DM delivery with real bot credentials and a production Feishu tenant.

@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 19, 2026
@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 19, 2026, 8:07 PM ET / 00:07 UTC.

Summary
The PR changes Feishu streaming-card startup to pass the DM-aware reply/root routing fields and adds dispatcher regression tests for ordinary DM suppression and group/thread preservation.

PR surface: Source +4, Tests +40. Total +44 across 2 files.

Reproducibility: yes. from source: current main passes raw streaming reply/root metadata even when the caller marks ordinary DMs to suppress reply metadata. I did not run live Feishu delivery in this read-only review.

Review metrics: 1 noteworthy metric.

  • Feishu streaming routing fields: 2 changed: replyToMessageId and rootId. These fields decide whether streaming cards use reply, root_create, or direct create mode, which is the merge-relevant behavior.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #94922
Summary: This PR is one candidate fix for the open Feishu DM streaming-card reply-mode issue; sibling PRs attempt the same root-cause repair and need canonical selection.

Members:

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

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🦞 diamond lobster
Result: blocked until stronger real behavior proof is added.

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

Rank-up moves:

  • [P1] Add redacted live Feishu DM/client proof, or obtain an explicit maintainer proof override.
  • Pick one canonical same-root Feishu PR before landing.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body provides source-level terminal output, but it does not show real Feishu DM delivery or the client-visible Reply-to label disappearing; redacted live proof or maintainer override is still needed, with private details redacted before posting. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Mantis proof suggestion
A real Feishu client proof would materially verify the visible Reply-to label behavior and thread preservation. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis visual task: verify in a real Feishu DM that streaming card replies show no Reply-to label, while group and P2P direct-thread replies stay threaded.

Risk before merge

  • [P1] Live Feishu DM delivery has not been shown after this patch, so the visible Reply-to label disappearing and group/direct-thread preservation still need redacted real-environment proof or an explicit maintainer override.
  • [P1] Three same-root candidate PRs are open for the canonical Feishu issue, so maintainers should pick one landing branch before merging duplicate fixes.

Maintainer options:

  1. Require live Feishu proof before merge (recommended)
    Ask for redacted real Feishu evidence showing an ordinary DM streaming-card reply without the Reply-to label and a group or P2P direct-thread reply still routed correctly.
  2. Accept a maintainer proof override
    A maintainer with Feishu credentials can explicitly own the risk after independently verifying the UI behavior or deciding source-level proof is sufficient for this channel fix.
  3. Pause behind a proven sibling
    If another same-root PR gets stronger live proof first, pause this branch or close it as superseded after the proven path lands.

Next step before merge

  • [P1] The remaining action is maintainer live Feishu proof or proof override plus canonical branch selection among open same-root fixes, not an automatable code repair.

Security
Cleared: The diff only changes Feishu dispatcher routing and colocated tests; it does not touch dependencies, workflows, secrets, packaging, or install scripts.

Review details

Best possible solution:

Land one canonical Feishu fix that suppresses ordinary-DM streaming-card reply metadata while preserving group/topic and P2P direct-thread routing, with live Feishu proof or maintainer override.

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

Yes from source: current main passes raw streaming reply/root metadata even when the caller marks ordinary DMs to suppress reply metadata. I did not run live Feishu delivery in this read-only review.

Is this the best way to solve the issue?

Yes: deriving streaming.start options from the existing skipReplyToInMessages decision is the narrow owner-boundary fix. Disabling streaming or changing global Feishu reply policy would be broader.

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This is a bounded Feishu channel message-routing bug fix with visible DM behavior impact.
  • merge-risk: 🚨 message-delivery: The patch intentionally changes Feishu streaming-card routing mode, so live message-delivery behavior should be proven before merge.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦞 diamond lobster.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body provides source-level terminal output, but it does not show real Feishu DM delivery or the client-visible Reply-to label disappearing; redacted live proof or maintainer override is still needed, with private details redacted before posting. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +4, Tests +40. Total +44 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 6 2 +4
Tests 1 40 0 +40
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 46 2 +44

What I checked:

Likely related people:

  • Vincentwei1021: Merged PR history for Feishu DM no-reply-to established the ordinary-DM invariant this PR extends to streaming cards. (role: introduced prior DM reply suppression behavior; confidence: high; commits: d9230b13a4ad; files: extensions/feishu/src/bot.ts, extensions/feishu/src/reply-dispatcher.ts)
  • qiangu: Merged Feishu streaming-card reply work routed ordinary final replies through the streaming-card path implicated by this report. (role: streaming-card reply-path contributor; confidence: high; commits: 3b8ab4e11232; files: extensions/feishu/src/reply-dispatcher.ts, extensions/feishu/src/reply-dispatcher.test.ts, extensions/feishu/src/streaming-card.ts)
  • xzq-xu: PR history credits this handle with the original Feishu Card Kit streaming-card support that owns the affected streaming session surface. (role: original streaming-card contributor; confidence: medium; commits: 65be9ccf63f3; files: extensions/feishu/src/reply-dispatcher.ts, extensions/feishu/src/streaming-card.ts)
  • vincentkoc: Recent Feishu caller/streaming blame and merged P2P direct-message thread history point to this handle as an adjacent routing owner. (role: recent area contributor and merger; confidence: medium; commits: 59a93a817ff9, c692fabeba07; files: extensions/feishu/src/reply-dispatcher.ts, extensions/feishu/src/streaming-card.ts, extensions/feishu/src/bot.ts)
  • LiaoyuanNing: P2P direct-message thread commit history credits this contributor for adjacent behavior this PR must preserve. (role: adjacent direct-thread contributor; confidence: medium; commits: c692fabeba07; files: extensions/feishu/src/bot.ts, extensions/feishu/src/bot.test.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.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. 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
@ZOOWH
ZOOWH force-pushed the fix/94922-feishu-dm-reply-mode branch from 9989775 to 651f77f Compare June 19, 2026 11:51
@ZOOWH

ZOOWH commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Request for maintainer proof override

This PR is blocked on 📣 needs proof because it lacks live Feishu DM screenshots. We do not have Feishu enterprise credentials to produce them.

However, this fix is strictly source-aligned — it is a one-line change that applies the same sendReplyToMessageId guard that every other Feishu send path (plain-text, media, structured card) already uses, to the streaming card start() — the single path that was missing it. The behavioral outcome is determined entirely by resolveStreamingCardSendMode, which returns "create" (no Reply-to label) when replyToMessageId=undefined and "reply" (correct threading) when present.

Why a proof override is reasonable here:

  1. The fix aligns one outlier path with 3+ already-proven paths that use the same sendReplyToMessageId value
  2. The skipReplyToInMessages guard logic is unchanged — it already works correctly on every other path
  3. P2P direct-thread replies are preserved because skipReplyToInMessages keeps sendReplyToMessageId set for directThreadReply scenarios
  4. A regression test asserts replyToMessageId=undefined is passed to streaming.start() when skipReplyToInMessages=true

A maintainer with Feishu credentials (e.g. @vincentkoc, @steipete) can override by:

  • Testing in a real Feishu DM and confirming the Reply-to label disappears
  • Posting @openclaw-mantis visual task: verify in a real Feishu DM that a streaming card reply appears without a Reply-to label while a P2P direct-thread reply still stays threaded.

Alternatively, if the sibling PR #94925 gets live proof first, we can close this in favor of the proven branch — but the code quality here is higher (🦞 patch quality per ClawSweeper) and the fix is identical in logic.

@ZOOWH
ZOOWH force-pushed the fix/94922-feishu-dm-reply-mode branch from 651f77f to 9d7a35b Compare June 19, 2026 17:00
@ZOOWH
ZOOWH requested a review from a team as a code owner June 19, 2026 17:00
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: discord Channel integration: discord channel: googlechat Channel integration: googlechat channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: whatsapp-web Channel integration: whatsapp-web channel: zalouser Channel integration: zalouser app: ios App: ios app: web-ui App: web-ui gateway Gateway runtime cli CLI command changes labels Jun 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added size: XS proof: supplied External PR includes structured after-fix real behavior proof. and removed app: ios App: ios app: web-ui App: web-ui gateway Gateway runtime cli CLI command changes scripts Repository scripts commands Command implementations agents Agent runtime and tooling channel: qqbot extensions: codex plugin: google-meet extensions: chutes extensions: github-copilot extensions: google extensions: mistral extensions: openrouter extensions: xai channel: sms Channel integration: sms size: XL triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 19, 2026
@ZOOWH

ZOOWH commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

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

@ZOOWH
ZOOWH force-pushed the fix/94922-feishu-dm-reply-mode branch from 678ff82 to fc71af1 Compare June 19, 2026 21:55
@ZOOWH

ZOOWH commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Strengthened real behavior proof: now calling the actual production function resolveStreamingCardSendMode from source via node --import tsx with before-and-after comparison (FAIL on upstream/main → PASS on PR branch). Also removed the Co-Authored-By line from the commit.

@clawsweeper

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

ZOOWH added 2 commits June 20, 2026 07:50
…M chats

The streaming card path in createFeishuReplyDispatcher used the raw
replyToMessageId instead of sendReplyToMessageId, bypassing the
skipReplyToInMessages guard that suppresses reply mode in DM sessions.
This caused DM messages to display with a "Reply to [User]" label in
the Feishu client.

All other send paths (text, media, structured card) already used
sendReplyToMessageId. The streaming card was the only outlier.

Closes openclaw#94922
…rt() in DM chats

In DM chats (skipReplyToInMessages=true), the streaming card start()
previously received the raw replyToMessageId and rootId, causing
resolveStreamingCardSendMode to choose "reply" or "root_create" mode
instead of "create". This mismatched the behavior of plain-text and
static-card sends, which omit reply metadata in DMs.

- Pass sendReplyToMessageId (already undefined in DM) to streaming.start()
- Add streamingRootId: undefined in DM (skipReplyToInMessages=true),
  preserving rootId for group/thread streaming
- Add regression tests for DM (both fields omitted) and group (both
  preserved) streaming card send modes

Closes openclaw#94922
@ZOOWH

ZOOWH commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

Closing this PR — unable to provide real Feishu environment proof that ClawSweeper requires for message-delivery merge-risk. Without a Feishu test app, the proof ceiling is stuck at supplied but not sufficient.

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

Labels

channel: feishu Channel integration: feishu 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: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: XS 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.

[Bug]: Feishu Plugin Incorrectly Uses Reply Mode in Direct (DM) Chats

1 participant