Skip to content

fix(telegram): suppress bare Reasoning: prefix leak during Gemini streaming#37892

Closed
okuyam2y wants to merge 2 commits intoopenclaw:mainfrom
okuyam2y:fix/gemini-reasoning-leak
Closed

fix(telegram): suppress bare Reasoning: prefix leak during Gemini streaming#37892
okuyam2y wants to merge 2 commits intoopenclaw:mainfrom
okuyam2y:fix/gemini-reasoning-leak

Conversation

@okuyam2y
Copy link
Copy Markdown
Contributor

@okuyam2y okuyam2y commented Mar 6, 2026

Summary

Fixes #37890

  • Add guard for bare Reasoning: / Reasoning:\n during streaming partials
  • trim() collapses "Reasoning:\n" to "Reasoning:" which doesn't match REASONING_MESSAGE_PREFIX and falls through to answerText, leaking the prefix into user-visible output
  • The new regex /^Reasoning:?\s*$/ catches these partial chunks and returns {} (suppress) before they reach the answer path

Test plan

  • Added test cases for "Reasoning:", "Reasoning:\n", and "Reasoning: \n"
  • Added test confirming multi-paragraph raw Gemini reasoning stays classified as reasoningText
  • pnpm exec vitest run src/telegram/reasoning-lane-coordinator.test.ts — 6 tests pass
  • pnpm check passes

cc @AyaanZaidi — you recently worked on draft materialize in this area (#36746)

@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram size: XS labels Mar 6, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 6, 2026

Greptile Summary

This PR fixes a prefix leak in the Telegram Gemini streaming path where bare Reasoning:\n partial chunks were being surfaced as user-visible answer text. The root cause was that text.trim() collapses "Reasoning:\n" to "Reasoning:", which no longer matches the REASONING_MESSAGE_PREFIX constant ("Reasoning:\n"), causing it to fall through to the answerText path.

Changes:

  • A new regex guard /^Reasoning:?\s*$/ is inserted in splitTelegramReasoningText to suppress these bare prefix chunks before they can reach the answerText fallback
  • Two new test cases are added: one verifying suppression of "Reasoning:" / "Reasoning:\n" / "Reasoning: \n", and one confirming that "Reasoning:\n" followed by actual content is still correctly classified as reasoningText

Issue found:

  • The colon in the regex is optional (:?), meaning the pattern also matches the plain string "Reasoning" (no colon). This is over-broad — if a streaming chunk contains exactly the word "Reasoning" (without colon), it will be silently suppressed rather than emitted as answer text. The fix should use /^Reasoning:\s*$/ (colon required) to match only the intended case. Additionally, \s* before $ is redundant since the check is applied to trimmed.

Confidence Score: 4/5

  • Safe to merge after fixing the over-broad :? in the regex on line 74, which could silently suppress legitimate "Reasoning" (no colon) answer chunks.
  • The fix is targeted and the tests are thorough and correct. The only issue is the optional colon (:?) in the guard regex making it slightly over-broad — suppressing bare "Reasoning" in addition to "Reasoning:". This is a low-probability edge case (a real answer of just "Reasoning" is rare) but is still an unintended behavior change worth correcting before merge.
  • src/telegram/reasoning-lane-coordinator.ts line 74 — the regex :? should be changed to require the colon.

Last reviewed commit: e71a7ec

// Suppress partial "Reasoning:" arriving during streaming before any
// actual reasoning content follows (trim() collapses "Reasoning:\n" to
// "Reasoning:" which would otherwise fall through to answerText).
if (/^Reasoning:?\s*$/.test(trimmed)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex /^Reasoning:?\s*$/ makes the colon optional, so it also suppresses the bare string "Reasoning" (no colon). This is broader than the stated intent ("suppress partial Reasoning: chunks") and would silently drop any streaming chunk that is exactly the word "Reasoning".

Since REASONING_MESSAGE_PREFIX is "Reasoning:\n" (colon required), the guard should only match the colon form:

Suggested change
if (/^Reasoning:?\s*$/.test(trimmed)) {
if (/^Reasoning:\s*$/.test(trimmed)) {

Additionally, \s* before $ is redundant here because this is applied to trimmed (the result of text.trim()), which already has no trailing whitespace. The guard still works correctly because \s* matches zero characters, but /^Reasoning:$/.test(trimmed) is equally correct and more self-documenting for this specific case.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/telegram/reasoning-lane-coordinator.ts
Line: 74

Comment:
The regex `/^Reasoning:?\s*$/` makes the colon optional, so it also suppresses the bare string `"Reasoning"` (no colon). This is broader than the stated intent ("suppress partial `Reasoning:` chunks") and would silently drop any streaming chunk that is exactly the word `"Reasoning"`.

Since `REASONING_MESSAGE_PREFIX` is `"Reasoning:\n"` (colon required), the guard should only match the colon form:

```suggestion
  if (/^Reasoning:\s*$/.test(trimmed)) {
```

Additionally, `\s*` before `$` is redundant here because this is applied to `trimmed` (the result of `text.trim()`), which already has no trailing whitespace. The guard still works correctly because `\s*` matches zero characters, but `/^Reasoning:$/.test(trimmed)` is equally correct and more self-documenting for this specific case.

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

@okuyam2y
Copy link
Copy Markdown
Contributor Author

okuyam2y commented Mar 6, 2026

Fixed in 6d1bbba — changed :? to require the colon: /^Reasoning:\s*$/

Good catch, thanks.

@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 20, 2026
@openclaw-barnacle
Copy link
Copy Markdown

Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.

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

Labels

channel: telegram Channel integration: telegram size: XS stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gemini raw reasoning text leaks into answer during Telegram DM streaming

1 participant