Skip to content

fix(feishu): send full streaming card content#88871

Open
charles-openclaw wants to merge 1 commit into
openclaw:mainfrom
charles-openclaw:fix-feishu-streaming-card-overwrite
Open

fix(feishu): send full streaming card content#88871
charles-openclaw wants to merge 1 commit into
openclaw:mainfrom
charles-openclaw:fix-feishu-streaming-card-overwrite

Conversation

@charles-openclaw

Copy link
Copy Markdown
Contributor

Summary

  • Fixes Feishu streaming card updates so the CardKit content endpoint receives the full visible markdown text on each update.
  • Preserves the existing replace path for final closeouts that remove transient streamed status text.
  • Updates focused Feishu streaming-card tests to assert full-content PUT payloads and retry behavior.

Linked context

Closes #88867.

Real behavior proof (required for external PRs)

  • Behavior or issue addressed: Feishu streaming cards displayed only the newest suffix/last character because the CardKit content update endpoint overwrites card content, while OpenClaw was sending only deltas.
  • Real environment tested: contributor checkout, local Feishu streaming-card unit harness.
  • Exact steps or command run after this patch: PNPM_CONFIG_OFFLINE=true corepack pnpm test extensions/feishu/src/streaming-card.test.ts -- --reporter=dot.
  • Evidence after fix: focused test assertions now verify throttled, natural-boundary, and retry updates send hello small, hello!, hello world, and hello world! as complete card content.
  • Observed result after fix: 1 Vitest shard passed, 17 tests passed.
  • What was not tested: live Feishu/Lark tenant streaming card flow.
  • Proof limitations or environment constraints: this contributor environment does not have safe Feishu app credentials or a tenant/channel for live CardKit proof.

Tests and validation

  • PNPM_CONFIG_OFFLINE=true corepack pnpm test extensions/feishu/src/streaming-card.test.ts -- --reporter=dot
  • git diff --check

Risk checklist

  • Did user-visible behavior change? Yes.
  • Did config, environment, or migration behavior change? No.
  • Did security, auth, secrets, network, or tool execution behavior change? No.
  • Highest-risk area: Feishu streaming card rendering cadence/content. Mitigation: the change only affects the content string sent to the existing CardKit update endpoint, and focused tests cover normal updates plus retry after failed/non-OK updates.

Current review state

Ready for review. Live Feishu proof still needs maintainer-side or credentialed tenant validation.

@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: XS triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels Jun 1, 2026
@clawsweeper

clawsweeper Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed May 31, 2026, 10:33 PM ET / 02:33 UTC.

Summary
The PR changes Feishu streaming-card updates to send full markdown content to the CardKit content endpoint and updates focused streaming-card tests from suffix payloads to full-content payloads.

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

Reproducibility: yes. at source level: current main slices Feishu CardKit updates down to suffixes while the official streaming-card guide describes sending full text and letting the platform compute the visible increment. I did not run a live Feishu/Lark tenant reproduction in this read-only review.

Review metrics: 1 noteworthy metric.

  • CardKit Payload Mode: 1 content endpoint changed from suffix delta to full text. This is the externally visible Feishu rendering contract that live proof should verify before merge.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof from a real setup 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/Lark proof showing a streamed multi-chunk card renders the full final content.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body provides a focused Vitest run but explicitly says no live Feishu/Lark tenant flow was tested; redacted screenshot, recording, logs, or terminal/live output from a real tenant should be added before merge and updating the PR body should trigger rereview. 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.

Risk before merge

  • [P1] No live Feishu/Lark tenant proof shows the rendered streaming card after this patch; the available proof is a unit harness that verifies outbound payloads, not client rendering.

Maintainer options:

  1. Run Credentialed Feishu Proof (recommended)
    Use a redacted Feishu/Lark tenant to stream a multi-update answer and show the card contains the full final markdown instead of only suffixes.
  2. Accept Official-Docs Plus Harness Proof
    A maintainer with CardKit confidence may intentionally land with the official contract and focused payload tests while recording that live tenant proof was unavailable.

Next step before merge

  • [P1] The remaining action is maintainer-side live Feishu/Lark validation or proof override, not an automated code repair.

Security
Cleared: The diff only changes Feishu CardKit content payload construction and focused tests; it adds no dependencies, scripts, permissions, secrets handling, or new code execution path.

Review details

Best possible solution:

Land the focused payload fix after a credentialed Feishu/Lark streaming-card smoke test or an explicit maintainer proof override, then let the linked bug close through the merged PR.

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

Yes at source level: current main slices Feishu CardKit updates down to suffixes while the official streaming-card guide describes sending full text and letting the platform compute the visible increment. I did not run a live Feishu/Lark tenant reproduction in this read-only review.

Is this the best way to solve the issue?

Yes, this is the narrowest maintainable fix I found: the changed helper is the single payload decision point before updateCardContent, and the replace path for final rewrites remains separate. The remaining gap is live external proof, not a better code location.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add P1: The linked bug reports Feishu streaming cards showing only the newest suffix for affected users, breaking a real channel workflow when streaming is enabled.
  • add merge-risk: 🚨 message-delivery: The PR changes the text payload sent to Feishu CardKit, so incorrect behavior would directly affect delivered chat-card content.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body provides a focused Vitest run but explicitly says no live Feishu/Lark tenant flow was tested; redacted screenshot, recording, logs, or terminal/live output from a real tenant should be added before merge and updating the PR body should trigger rereview. 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.

Label justifications:

  • P1: The linked bug reports Feishu streaming cards showing only the newest suffix for affected users, breaking a real channel workflow when streaming is enabled.
  • merge-risk: 🚨 message-delivery: The PR changes the text payload sent to Feishu CardKit, so incorrect behavior would directly affect delivered chat-card content.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body provides a focused Vitest run but explicitly says no live Feishu/Lark tenant flow was tested; redacted screenshot, recording, logs, or terminal/live output from a real tenant should be added before merge and updating the PR body should trigger rereview. 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 -1, Tests 0. Total -1 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 3 4 -1
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 12 -1

What I checked:

  • Current main sends suffix-only content: On current main, resolveStreamingCardAppendContent returns nextText.slice(previousText.length) when the new text starts with the previously sent text, and updateCardContent passes that string as the CardKit content payload. (extensions/feishu/src/streaming-card.ts:157, 4932391e8a78)
  • CardKit update call boundary: The Feishu streaming session sends PUT /cardkit/v1/cards/{cardId}/elements/content/content with JSON body { content, sequence, uuid }, so the helper output is exactly the external CardKit text update payload. (extensions/feishu/src/streaming-card.ts:350, 4932391e8a78)
  • Patch changes the payload mode: The PR patch removes the suffix-slicing branch and returns nextText after the unchanged/empty guard, with tests updated to expect hello small, hello!, hello world, and hello world! as full payloads. (extensions/feishu/src/streaming-card.ts:158, 1c4a18da734f)
  • Existing focused tests prove the current mismatch: The base tests currently assert suffix payloads for throttled, natural-boundary, failed-update, and non-OK retry paths; the PR changes those same cases to full-content payloads without changing unrelated flow control. (extensions/feishu/src/streaming-card.test.ts:181, 4932391e8a78)
  • External CardKit contract check: The official Feishu streaming-card guide says streaming text updates pass full text content, and if old text is a prefix of the new text the platform computes and renders the added suffix itself.
  • Linked bug remains paired with this PR: The PR body says it closes [Bug]: Feishu streaming card: only the last character displayed after streaming completes #88867, whose supplied context reports 100% reproduction for Feishu streaming cards showing only the last suffix while chat-list preview has the full text. (1c4a18da734f)

Likely related people:

  • hclsys: GitHub commit history shows f436b43 as the Feishu CardKit text-delta change that introduced the suffix-oriented streaming path being corrected here. (role: introduced behavior; confidence: high; commits: f436b4310a0c; files: extensions/feishu/src/streaming-card.ts)
  • qiangu: GitHub commit history shows 3b8ab4e recently moved ordinary Feishu streaming replies through CardKit cards, making this streaming-card path broadly visible. (role: recent area contributor; confidence: high; commits: 3b8ab4e11232; files: extensions/feishu/src/streaming-card.ts, extensions/feishu/src/reply-dispatcher.test.ts)
  • ArthurNie: GitHub commit history shows 7c15c27 recently changed Feishu visible-reply fallback behavior around streaming closeout and delivery state. (role: recent adjacent contributor; confidence: medium; commits: 7c15c2765ebb; files: extensions/feishu/src/streaming-card.ts, extensions/feishu/src/reply-dispatcher.test.ts)
  • steipete: Local and GitHub history show repeated recent Feishu streaming-card and test maintenance, including token expiry, helper export trimming, and lint/readability updates in this area. (role: recent area contributor; confidence: medium; commits: 04de01f8cfe9, b388209eaf72, 27dde7a4d69b; files: extensions/feishu/src/streaming-card.ts, extensions/feishu/src/reply-dispatcher.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. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 1, 2026
@mikeyoubeach

Copy link
Copy Markdown

Tested on real Feishu environment - v2026.5.26 and v2026.6.1 both have this issue. Streaming cards only show the last character. This fix is needed across all versions. Please merge.

@openclawGit200

Copy link
Copy Markdown

Live environment evidence (helps clear needs proof)

I hit this exact symptom today on OpenClaw 2026.6.1 with @openclaw/feishu@2026.6.1, deployed against a real Feishu/Lark tenant (app cli_a92a9ce81b781ccd, owner ou_9e78476eb905d306f9f7c835bf978e8b).

Symptom

Agent-generated replies rendered as a single trailing character (e.g. ?) in the Feishu DM chat window, while OpenClaw's own log showed the streaming card starting and closing normally.

Two affected streaming cards (from /tmp/openclaw/openclaw-2026-06-10.log)

1. cardId 7649427059472813028 (messageId om_x100b6db7d8f51cacb321400ce221714)

  • 00:03:48 Started streaming
  • 00:04:07 Update failed: Error: Update card content failed with HTTP 500
  • 00:04:10 dispatch complete (queuedFinal=true, replies=1)
  • 00:04:11 Closed streaming
  • Net result: card frozen, single character visible to the user.

2. cardId 7649427607708978163 (messageId om_x100b6db7d0fa14a4b3236710ada2e67)

Root cause confirmed

resolveStreamingCardAppendContent in extensions/feishu/src/streaming-card.ts computes a delta slice assuming the CardKit PUT /cardkit/v1/cards/{cardId}/elements/content/content endpoint accepts appends. It doesn't — the endpoint overwrites the markdown element body. When sentText falls behind (due to an HTTP 500 or the 5-min cap), subsequent updates send garbage slices that overwrite the entire card content with a single character — exactly matching the user-visible "last character only" symptom.

Local mitigation applied

I applied this PR's diff directly to node_modules/@openclaw/feishu/dist/monitor.account-BvKcwxaW.js (lines 900–903) and restarted the gateway. Live confirmation from a fresh DM is pending — will report back once I have a clean before/after comparison from a new streaming card.

Suggestion for maintainers

Thanks for picking this up — happy to provide additional logs or run experiments against my tenant if helpful for upstream validation.

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. P1 High-priority user-facing bug, regression, or broken workflow. 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. triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Feishu streaming card: only the last character displayed after streaming completes

3 participants