Skip to content

fix(feishu): preserve streaming card content#90181

Merged
sliverp merged 2 commits into
openclaw:mainfrom
mushuiyu886:feat/issue-90164-feishu-streaming-card
Jun 4, 2026
Merged

fix(feishu): preserve streaming card content#90181
sliverp merged 2 commits into
openclaw:mainfrom
mushuiyu886:feat/issue-90164-feishu-streaming-card

Conversation

@mushuiyu886
Copy link
Copy Markdown
Contributor

@mushuiyu886 mushuiyu886 commented Jun 4, 2026

Summary

  • Fix Feishu streaming-card updates so CardKit receives cumulative visible markdown instead of suffix-only content.
  • Update Feishu streaming regression tests for throttled, natural-boundary, failed-update, and non-OK retry paths to assert cumulative request bodies.
  • Scope: Feishu streaming CardKit content updates only; no config/default changes and no non-streaming Feishu delivery changes.

Fixes #90164

Real behavior proof

Behavior addressed: Feishu streaming-card update calls must preserve the cumulative reply text in the CardKit content element. Latest origin/main still sends only the suffix when the visible text is already hello and the next streamed snapshot is hello!; the patched PR head sends the cumulative hello! body for the same FeishuStreamingSession.update() path.

Real environment tested: Local Linux detached worktree at latest origin/main (344417c0de19c23ceb68a88c044b473fe1847f8f) and local Linux patched PR head (d87c6c3ed4530664cb4316219a21b2c645785fe7). Both runs imported the real extensions/feishu/src/streaming-card.ts production code and used production fetchWithSsrFGuard against a live local Feishu-compatible HTTP server that implemented the tenant-token and CardKit content update endpoints. The fetch boundary was not mocked; the local HTTP server captured the actual token request and CardKit PUT request bodies.

Exact steps or command run after this patch:

env -C "/home/0668001029/openclaw-worktrees/issue-90164-main-proof" OPENCLAW_REPO_ROOT="/home/0668001029/openclaw-worktrees/issue-90164-main-proof" PROOF_RUN_LABEL="latest-main-344417c0de" HTTP_PROXY="" HTTPS_PROXY="" ALL_PROXY="" NO_PROXY="127.0.0.1,localhost" corepack [email protected] exec tsx "/home/0668001029/openclaw-evidence/openclaw-issue-90164-evidence/feishu-compatible-http-proof.ts" > "/home/0668001029/openclaw-evidence/openclaw-issue-90164-evidence/compatible-http-main-output.txt"

env -C "/home/0668001029/openclaw-worktrees/issue-90164" OPENCLAW_REPO_ROOT="/home/0668001029/openclaw-worktrees/issue-90164" PROOF_RUN_LABEL="patched-head" HTTP_PROXY="" HTTPS_PROXY="" ALL_PROXY="" NO_PROXY="127.0.0.1,localhost" corepack [email protected] exec tsx "/home/0668001029/openclaw-evidence/openclaw-issue-90164-evidence/feishu-compatible-http-proof.ts" > "/home/0668001029/openclaw-evidence/openclaw-issue-90164-evidence/compatible-http-patched-output.txt"

env -C "/home/0668001029/openclaw-worktrees/issue-90164" node scripts/run-vitest.mjs extensions/feishu/src/streaming-card.test.ts extensions/feishu/src/reply-dispatcher.test.ts --reporter=verbose > "/home/0668001029/openclaw-evidence/openclaw-issue-90164-evidence/focused-tests-output.txt"

git -C "/home/0668001029/openclaw-worktrees/issue-90164" diff --check > "/home/0668001029/openclaw-evidence/openclaw-issue-90164-evidence/diff-check-output.txt"

Evidence after fix:

Latest origin/main (344417c0de19c23ceb68a88c044b473fe1847f8f), live local Feishu-compatible HTTP server:
PROOF_RUN_LABEL=latest-main-344417c0de
FETCH_BOUNDARY=production fetchWithSsrFGuard to local Feishu-compatible HTTP server
SERVER_RECEIVED_PATHS=/open-apis/auth/v3/tenant_access_token/internal,/open-apis/cardkit/v1/cards/card_proof/elements/content/content
TOKEN_REQUEST_COUNT=1
CARDKIT_UPDATE_METHOD=PUT
CARDKIT_UPDATE_AUTHORIZATION=Bearer <redacted>
CARDKIT_UPDATE_CONTENT=!
CARDKIT_UPDATE_BODY={"content":"!","sequence":2,"uuid":"s_card_proof_2"}

Patched PR head (d87c6c3ed4530664cb4316219a21b2c645785fe7), same live local Feishu-compatible HTTP server proof:
PROOF_RUN_LABEL=patched-head
FETCH_BOUNDARY=production fetchWithSsrFGuard to local Feishu-compatible HTTP server
SERVER_RECEIVED_PATHS=/open-apis/auth/v3/tenant_access_token/internal,/open-apis/cardkit/v1/cards/card_proof/elements/content/content
TOKEN_REQUEST_COUNT=1
CARDKIT_UPDATE_METHOD=PUT
CARDKIT_UPDATE_AUTHORIZATION=Bearer <redacted>
CARDKIT_UPDATE_CONTENT=hello!
CARDKIT_UPDATE_BODY={"content":"hello!","sequence":2,"uuid":"s_card_proof_2"}

Focused regression suite on patched PR head:
Test Files  2 passed (2)
Tests       94 passed (94)

Whitespace check on patched PR head:
git diff --check produced no output.

Observed result after fix: The same real FeishuStreamingSession.update() path that sent suffix-only content: "!" on latest origin/main now sends cumulative content: "hello!" on the patched head, while still authenticating once and using the CardKit content element PUT endpoint. This preserves the full visible streaming-card text instead of overwriting the element with only the newest chunk.

What was not tested: No live Feishu/Lark tenant or real user chat was used, and no real Feishu credentials were exercised. The proof used a live local Feishu-compatible HTTP server to verify the production HTTP boundary and request payloads without external credentials.

Regression Test Plan

  • extensions/feishu/src/streaming-card.test.ts now asserts cumulative CardKit content bodies for throttled flushes, natural-boundary immediate pushes, failed update retries, and non-OK update retries.
  • extensions/feishu/src/reply-dispatcher.test.ts was run with the streaming-card tests to keep the dispatcher-to-session streaming path covered.
  • Focused command run: env -C "/home/0668001029/openclaw-worktrees/issue-90164" node scripts/run-vitest.mjs extensions/feishu/src/streaming-card.test.ts extensions/feishu/src/reply-dispatcher.test.ts --reporter=verbose.

Root Cause

  • FeishuStreamingSession.update() received cumulative merged streaming text, but converted it back to suffix-only content before calling the CardKit content element update endpoint.
  • CardKit content updates replace the target element content, so sending only the suffix could leave users seeing only the last character or chunk.
  • The fix keeps the existing merge/throttle/retry behavior and changes the CardKit update payload to the cumulative text that the session is already tracking.

@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 4, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Jun 4, 2026

Codex review: needs maintainer review before merge. Reviewed June 4, 2026, 2:09 AM ET / 06:09 UTC.

Summary
The PR removes Feishu streaming-card suffix extraction so CardKit text updates receive cumulative markdown, and updates streaming-card tests to expect cumulative payloads.

PR surface: Source -14, Tests 0. Total -14 across 2 files.

Reproducibility: yes. A high-confidence source path is FeishuStreamingSession.update() with current/sent text hello and next text hello!: current main sends content: "!", and the PR body supplies before/after compatible-HTTP output for the same path.

Review metrics: none identified.

Merge readiness
Overall: 🦞 diamond lobster
Proof: 🦞 diamond lobster
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

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

Risk before merge

  • [P1] No live Feishu/Lark tenant rendering was exercised; the supplied proof covers the production fetch boundary against a compatible local HTTP server and matches the official full-text API contract, leaving only a small service-side rendering gap.

Maintainer options:

  1. Decide the mitigation before merge
    Land the focused Feishu plugin fix after normal CI and maintainer merge gates, keeping streaming CardKit updates cumulative.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • No ClawSweeper repair lane is needed because the PR is reviewable as-is with sufficient proof and no blocking findings.

Security
Cleared: The diff changes only Feishu streaming-card payload construction and tests, with no credential, dependency, workflow, or supply-chain surface added.

Review details

Best possible solution:

Land the focused Feishu plugin fix after normal CI and maintainer merge gates, keeping streaming CardKit updates cumulative.

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

Yes. A high-confidence source path is FeishuStreamingSession.update() with current/sent text hello and next text hello!: current main sends content: "!", and the PR body supplies before/after compatible-HTTP output for the same path.

Is this the best way to solve the issue?

Yes. Removing suffix extraction at the FeishuStreamingSession CardKit update boundary is the narrowest maintainable fix because the dispatcher already passes cumulative snapshots and the replacement path remains for non-prefix rewrites.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add P1: This fixes a shipped Feishu channel regression that can leave streaming-card users seeing only the newest chunk or character instead of the full reply.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body now includes before/after output from production fetchWithSsrFGuard against a live local Feishu-compatible HTTP server, showing current main sends ! and the patched head sends hello!.
  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body now includes before/after output from production fetchWithSsrFGuard against a live local Feishu-compatible HTTP server, showing current main sends ! and the patched head sends hello!.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: This fixes a shipped Feishu channel regression that can leave streaming-card users seeing only the newest chunk or character instead of the full reply.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body now includes before/after output from production fetchWithSsrFGuard against a live local Feishu-compatible HTTP server, showing current main sends ! and the patched head sends hello!.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body now includes before/after output from production fetchWithSsrFGuard against a live local Feishu-compatible HTTP server, showing current main sends ! and the patched head sends hello!.
Evidence reviewed

PR surface:

Source -14, Tests 0. Total -14 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 3 17 -14
Tests 1 8 8 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 11 25 -14

What I checked:

Likely related people:

  • xzq-xu: Commit 65be9cc introduced Feishu streaming-card support via CardKit API and added the core streaming-card module. (role: introduced behavior; confidence: high; commits: 65be9ccf63f3; files: extensions/feishu/src/streaming-card.ts, extensions/feishu/src/reply-dispatcher.ts)
  • Huaqing.Hao: Commit a5a7239 previously repaired Feishu streaming text preservation and added the mergeStreamingText tests used by this path. (role: prior streaming repair author; confidence: high; commits: a5a7239182d5; files: extensions/feishu/src/streaming-card.ts, extensions/feishu/src/streaming-card.test.ts)
  • rexl2018: Commit 3bf6ed1 hardened streaming merge semantics, final reply dedupe, and routing behavior around the same session and dispatcher boundary. (role: adjacent feature hardening author; confidence: high; commits: 3bf6ed181e03; files: extensions/feishu/src/streaming-card.ts, extensions/feishu/src/reply-dispatcher.ts, extensions/feishu/src/streaming-card.test.ts)
  • Vincent Koc: Recent Feishu runtime and streaming-adjacent commits touched this channel surface, including reasoning preview gating and runtime seam refactors. (role: recent area contributor; confidence: medium; commits: d609f71c9b74, ddd1c77b49ef; files: extensions/feishu/src/reply-dispatcher.ts, extensions/feishu/src/streaming-card.ts)
  • Peter Steinberger: Multiple recent Feishu refactors and the current-main source snapshot touch the same channel/runtime files, making this a likely routing candidate for follow-up review. (role: recent area contributor; confidence: medium; commits: 143ae5a5b070, 35d801a1e54f; files: extensions/feishu/src/reply-dispatcher.ts, extensions/feishu/src/streaming-card.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: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 4, 2026
@mushuiyu886
Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Jun 4, 2026

🦞🧹
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:

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 High-priority user-facing bug, regression, or broken workflow. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 4, 2026
@sliverp sliverp self-assigned this Jun 4, 2026
@sliverp sliverp force-pushed the feat/issue-90164-feishu-streaming-card branch from d87c6c3 to 22ead4d Compare June 4, 2026 07:57
@sliverp sliverp merged commit 1f1ce8a into openclaw:main Jun 4, 2026
16 checks passed
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 4, 2026
@sliverp
Copy link
Copy Markdown
Member

sliverp commented Jun 4, 2026

Landed via temp rebase onto main.

  • Gate: skipped per maintainer request
  • Land commit: 22ead4d
  • Merge commit: 1f1ce8a

Thanks @mushuiyu886!

SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request Jun 4, 2026
* fix(feishu): preserve streaming card content

* fix(feishu): preserve streaming card content (openclaw#90181) (thanks @mushuiyu886)

---------

Co-authored-by: sliverp <[email protected]>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request Jun 4, 2026
* fix(feishu): preserve streaming card content

* fix(feishu): preserve streaming card content (openclaw#90181) (thanks @mushuiyu886)

---------

Co-authored-by: sliverp <[email protected]>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request Jun 4, 2026
* fix(feishu): preserve streaming card content

* fix(feishu): preserve streaming card content (openclaw#90181) (thanks @mushuiyu886)

---------

Co-authored-by: sliverp <[email protected]>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request Jun 4, 2026
* fix(feishu): preserve streaming card content

* fix(feishu): preserve streaming card content (openclaw#90181) (thanks @mushuiyu886)

---------

Co-authored-by: sliverp <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 5, 2026
* fix(feishu): preserve streaming card content

* fix(feishu): preserve streaming card content (openclaw#90181) (thanks @mushuiyu886)

---------

Co-authored-by: sliverp <[email protected]>
traoremp pushed a commit to traoremp/openclaw that referenced this pull request Jun 5, 2026
* fix(feishu): preserve streaming card content

* fix(feishu): preserve streaming card content (openclaw#90181) (thanks @mushuiyu886)

---------

Co-authored-by: sliverp <[email protected]>
849261680 pushed a commit to 849261680/openclaw that referenced this pull request Jun 7, 2026
* fix(feishu): preserve streaming card content

* fix(feishu): preserve streaming card content (openclaw#90181) (thanks @mushuiyu886)

---------

Co-authored-by: sliverp <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: feishu Channel integration: feishu P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 2026.6.1 飞书流式回复仅显示最后一个字符(卡片被 update 而非 patch)

2 participants