fix(slack): keep thread label snippet truncation UTF-16 safe#101782
fix(slack): keep thread label snippet truncation UTF-16 safe#101782hugenshen wants to merge 1 commit into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 8, 2026, 1:45 AM ET / 05:45 UTC. Summary PR surface: Source +3, Tests +7. Total +10 across 3 files. Reproducibility: yes. source-level: current main has two Slack thread-label paths that truncate whitespace-collapsed starter text with raw Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused Slack helper-based fix after ordinary maintainer rebase and exact-head merge checks. Do we have a high-confidence way to reproduce the issue? Yes, source-level: current main has two Slack thread-label paths that truncate whitespace-collapsed starter text with raw Is this the best way to solve the issue? Yes. Reusing the existing AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against eb5d8f85c458. Label changesLabel justifications:
Evidence reviewedPR surface: Source +3, Tests +7. Total +10 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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
Review history (5 earlier review cycles)
|
ec0c968 to
718bbfa
Compare
Replace raw .slice(0, 80) thread label snippets with truncateUtf16Safe so emoji straddling the cap are dropped whole instead of leaving unpaired surrogates in Slack thread labels. Co-authored-by: Cursor <[email protected]>
718bbfa to
7c3661d
Compare
|
Squashed to a single commit and rebased on latest Test run @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review |
Consolidates #102007, #101818, and #101782. Co-authored-by: 杨浩宇0668001029 <[email protected]> Co-authored-by: NIO <[email protected]> Co-authored-by: Cursor <[email protected]>
|
Landed the canonical fix on The valid Slack thread-label truncation fix is included for both assistant-root and ordinary user/third-party-bot paths, and @hugenshen's authorship is preserved with a co-author trailer. I direct-landed the consolidation because this branch conflicted with current Proof on the landed patch:
Closing this PR as included in the landed main commit, not rejected. |
Consolidates openclaw#102007, openclaw#101818, and openclaw#101782. Co-authored-by: 杨浩宇0668001029 <[email protected]> Co-authored-by: NIO <[email protected]> Co-authored-by: Cursor <[email protected]>
Consolidates openclaw#102007, openclaw#101818, and openclaw#101782. Co-authored-by: 杨浩宇0668001029 <[email protected]> Co-authored-by: NIO <[email protected]> Co-authored-by: Cursor <[email protected]>
What Problem This Solves
Fixes an issue where Slack thread labels injected into the agent prompt context could contain invalid Unicode when thread starter messages contain emoji near the 80-character snippet boundary.
When a Slack thread reply arrives, OpenClaw builds a human-readable label for the thread (e.g.
"Slack thread #general: Great idea about the 🚀 launch…") and injects it into the LLM's context. The snippet is produced by collapsing whitespace then calling.slice(0, 80). Emoji and supplementary-plane characters are represented as two UTF-16 code units (a surrogate pair). If the cut point lands between the two halves, the result contains an isolated surrogate — invalid Unicode — which is then passed to the model as part of its context string.This affects two sibling helper functions:
prepareSlackThreadContextinprepare-thread-context.ts— buildsthreadLabelfor live thread repliesresolveSlackAssistantRootThreadLabel/formatSlackBotStarterThreadLabelinprepare-thread-context-root.ts— builds the label for assistant-root thread sessionsWhy This Change Was Made
Replace
.slice(0, 80)withtruncateUtf16Safe(..., 80)in both helpers. The surrounding.replace(/\s+/g, " ")and.trim()calls are unchanged; only the final truncation step is made surrogate-safe.truncateUtf16Safefromopenclaw/plugin-sdk/text-utility-runtimeis the established helper for this pattern throughout the codebase.Both sites are fixed in the same PR because they share the identical bug pattern and are tested together.
User Impact
Slack users who start threads with emoji-rich messages long enough to reach the 80-character boundary (common in practice — a single sentence with a few emoji easily hits this) would cause the agent to receive a malformed thread label in its prompt context. This can produce garbled context, confuse the model about the thread subject, or trigger a rejected API call on providers that validate Unicode input. After this fix, thread labels are always valid Unicode strings.
Evidence
prepare-thread-context.ts:228(post-fix):const snippet = truncateUtf16Safe(starter.text.replace(/\s+/g, " "), 80);—snippetis used on the next line asthreadLabel = \Slack thread ${params.roomLabel}: ${snippet}``.prepare-thread-context-root.ts:112(post-fix):const snippet = truncateUtf16Safe(params.starterText.replace(/\s+/g, " "), 80).trim();—snippetis returned as part of the label string\${base} (assistant root): ${snippet}``.truncateUtf16Safe(..., 80)— same length cap, surrogate-pair-safe.prepare-thread-context-root.test.ts.Behavior proof
1) Node runtime through production
formatSlackBotStarterThreadLabel(no Vitest mocks)Imports and calls the real exported helper from
prepare-thread-context-root.tson a surrogate-boundary starter (79 × 'a' + 🐱tail):This shows both fixed call-site shapes — assistant-root labels and live thread labels — emit surrogate-safe snippets at the 80 code-unit cap.
2) Call-site regression test
Regression asserts
formatSlackBotStarterThreadLabel({ roomLabel: "DM", starterText: "a".repeat(79) + "🐱tail" })drops the boundary emoji whole and leaves no unpaired surrogate in the final label.Proof gap: No redacted live Slack workspace transcript in this validation environment. The Node runtime call and regression cover the production label builders; optional supplemental proof is a redacted thread-reply log showing a long emoji-near-boundary starter produces a well-formed
threadLabel.