Skip to content

feat(telegram): Settings toggle to disable bot progress streaming - #157

Merged
KrasimirKralev merged 5 commits into
betafrom
feat/telegram-progress-toggle
May 29, 2026
Merged

feat(telegram): Settings toggle to disable bot progress streaming#157
KrasimirKralev merged 5 commits into
betafrom
feat/telegram-progress-toggle

Conversation

@KrasimirKralev

@KrasimirKralev KrasimirKralev commented May 28, 2026

Copy link
Copy Markdown
Contributor

What

Two related chat/Telegram improvements:

1. Telegram progress-streaming toggle. When the Telegram bot takes >5s on a task, OpenClaw streams live tool/research progress drafts (the "Bubbling…" headers + Web Search / Web Fetch / Firecrawl lines) to the chat before the final answer. For a chat bot that's noisy. This adds a user-facing toggle to turn it off.

Settings → Telegram → "Show research progress" — default ON (no behavior change for existing users; upgrading writes nothing). Off = the bot delivers the final answer only.

2. Chat reconnect overlay on any gateway restart. Previously the chat only showed the skill-install progress overlay for skill/provider changes; every other gateway bounce (a settings change, the new streaming toggle, a crash) just froze the WebSocket until the bare retry loop reconnected. Now the same animated overlay shows on any reconnect after a successful connection — the chat never looks frozen during a restart.

How

Telegram toggle

  • src/lib/openclaw-config.tsgetTelegramProgressStreaming() / setTelegramProgressStreaming(enabled). OpenClaw gates the progress draft on the channel's streaming mode, so OFF writes channels.telegram.streaming = { mode: "off" } and ON removes the override (restoring OpenClaw's default). Spreads the existing telegram object so botToken/enabled survive, and never writes dmPolicy/allowFrom (same security invariant as setTelegramToken).
  • /setup-api/telegram/streaming (new) — GET reads the current state, POST writes it + restartGateway(). Returns 502 "saved but restart failed" (mirroring ai-models/configure) so a restart hiccup doesn't read as a save failure.
  • SettingsApp.tsx — a role="switch" toggle in the Telegram connected card (only meaningful with a bot connected), optimistic with a pending spinner since the POST restarts the gateway.
  • i18nsettings.telegramProgress + settings.telegramProgressHint in all 10 locales.

Chat reconnect overlay

  • ChatPopup.tsx — the WS onClose now trips the reload overlay (reason 'restart') whenever the socket drops after a successful connection (connectedOnceRef), guarded against re-tripping mid-reconnect. The hello-resolve keeps the visible history for a plain restart (nothing about the session changed) and completes the progress bar, same as the provider flow.

/simplify pass

  • Extracted the byte-identical reload progress-timer setInterval (duplicated in onClose and the skill/provider handler) into one shared startReloadProgressTimer().
  • Narrowed makeHandler's param to 'skill' | 'provider' (the 'restart' reason is set directly in onClose).
  • Flattened the keepHistoryReload branch in the hello-resolve callback.
  • Settings toggle reverts to the captured prior value on failure instead of assuming !next.

Safety: gateway-pre-start.sh + config validator

  • gateway-pre-start.sh only strips dmPolicy/allowFrom on restart — it does not touch a streaming key, so the user's choice persists across reboots. (Confirmed.)
  • Validated on a real Jetson (OpenClaw 2026.5.22): set channels.telegram.streaming = {mode:"off"}, restarted the gateway → active, HTTP 200, no config-validation error. The telegram channel schema accepts the key (unlike the strict model-compat schema behind the earlier supportedReasoningEfforts brick).

Test plan

  • Helper logic covered by unit tests (6: default ON, off→mode:off preserving botToken/enabled/unknown keys, on→removes override, never writes dmPolicy/allowFrom)
  • streaming.mode="off" accepted by the pinned gateway (no brick) — verified live on a Jetson
  • Manual: toggle off in Settings, message the bot a slow task → final answer only (verified on nano)
  • Manual: chat reconnect overlay shows on gateway restart instead of freezing (verified on nano)
  • CI (test / e2e / e2e-install)
  • CodeRabbit review

Summary by CodeRabbit

  • New Features

    • Added Telegram progress streaming toggle in settings to control whether progress messages appear in chat.
  • Improvements

    • Enhanced chat reconnection handling with dedicated "Restarting chat..." status indicator during gateway restarts.
    • Improved reload behavior to better distinguish between different reconnection scenarios.

Review Change Stack

When the agent takes >5s on a task, OpenClaw streams live tool/research
progress drafts ("Bubbling…", Web Search/Fetch/Firecrawl lines) to the
Telegram chat before the final answer. That's noisy for a chat bot.

Adds Settings → Telegram → "Show research progress" (default ON, so no
behavior change for existing users). Turning it off makes the bot deliver
the final answer only.

- openclaw-config.ts: get/setTelegramProgressStreaming. Off writes
  channels.telegram.streaming = { mode: "off" } (OpenClaw gates the
  progress draft on streaming mode); On removes the override to restore
  the default. Spreads the existing telegram object so botToken/enabled
  survive; never writes dmPolicy/allowFrom (same invariant as
  setTelegramToken). gateway-pre-start.sh already leaves the streaming
  key untouched on restart, so the choice persists.
- New /setup-api/telegram/streaming route (GET reads, POST writes +
  restartGateway; 502 = saved-but-restart-failed like ai-models/configure).
- SettingsApp: a switch in the connected card, optimistic with a pending
  spinner (the POST restarts the gateway).
- i18n: settings.telegramProgress + Hint across all 10 locales.
- Unit tests for the get/set helpers (default ON, off→mode:off preserving
  botToken, on→removes override, never dmPolicy/allowFrom).
@KrasimirKralev
KrasimirKralev requested a review from a team as a code owner May 28, 2026 22:40
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@KrasimirKralev, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 44 minutes and 1 second. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 494d0384-a4f5-496d-a13d-d84e63e5e34c

📥 Commits

Reviewing files that changed from the base of the PR and between c6a97e2 and 737afeb.

📒 Files selected for processing (1)
  • src/components/ChatPopup.tsx
📝 Walkthrough

Walkthrough

Adds a Telegram progress streaming toggle (config schema, helpers, API GET/POST, UI switch with optimistic updates, translations, and tests) and refactors ChatPopup reconnect logic to treat gateway restarts separately from provider/skill reloads with a shared reload-progress timer.

Changes

Telegram Progress Streaming Feature

Layer / File(s) Summary
Configuration model and persistence
src/lib/openclaw-config.ts, src/tests/unit/openclaw-config.test.ts
OpenClawConfig.channels now includes optional streaming with mode. Adds getTelegramProgressStreaming() and setTelegramProgressStreaming(enabled) to read/persist the preference; tests cover defaulting, missing config, and preservation of unrelated channel fields.
Streaming API route handler
src/app/setup-api/telegram/streaming/route.ts
Adds dynamic Next.js route exporting GET (returns current streaming state, no-store) and POST (validates enabled boolean, persists via config helpers, attempts gateway restart; reports restart success or returns 502/restarted:false while preserving the saved setting).
Settings UI, state, and translations
src/components/SettingsApp.tsx, src/lib/translations.ts
Adds tri-state tgStreaming (null=loading) and tgStreamingPending; fetches current setting when Telegram section is shown; toggleTelegramStreaming posts updates with optimistic UI and selective revert (keeps optimistic value on HTTP 502); UI switch bound to state with aria attributes; new i18n keys added across multiple locales.

ChatPopup reconnect & reload UX

Layer / File(s) Summary
Reconnect overlay entry and labeling
src/components/ChatPopup.tsx
When WebSocket closes after a prior successful connection, set reloadReason = "restart", show reconnect overlay, and adjust retry budget for restarts.
Post-hello handling and history preservation
src/components/ChatPopup.tsx
Compute keepHistoryReload for provider and restart cases; preserve transcript when appropriate and avoid auto-sending skill-change context when history is kept.
Reload-progress timer refactor
src/components/ChatPopup.tsx
Extend reloadReason union to include restart, refactor progress logic into startReloadProgressTimer(), and invoke it from reload handlers; overlay label renders “Restarting chat…” for restarts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • ID-Robots/clawbox#83: Also modifies src/components/ChatPopup.tsx reconnect/reload overlay logic and reloadReason handling.
  • ID-Robots/clawbox#110: Also updates startReloadProgressTimer() and related reconnect/reload logic in ChatPopup.tsx.

Suggested reviewers

  • yalexx

Poem

🐰 I toggled streams with careful cheer,
Saved settings, pinged the gateway near,
A switch, a restart, UI bright and keen,
Transcripts kept and reboots seen,
Hop, click, rejoice — the rabbit's seen!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and accurately describes the main feature: a settings toggle for the Telegram bot to disable progress streaming.
Description check ✅ Passed The PR description comprehensively covers the changes, implementation details, test plan, and safety considerations, addressing all major sections of the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/telegram-progress-toggle

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown

CI Summary

✅ Tests

  • Result: passed
  • View run
  • Coverage: statements 70.76%, branches 60.21%, functions 66.7%, lines 72.91%

✅ E2E

✅ E2E Install

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Actionable comments posted: 0

The skill-install / provider-change flows already show a progress-bar
overlay ("Reloading skills…") while the gateway bounces and the chat WS
reconnects. Any other gateway restart — the new Telegram streaming
toggle, a settings change, a crash — just froze the chat behind the bare
"connecting" spinner.

Generalize it: in the WS onClose, if we'd already connected once
(connectedOnceRef) and aren't already mid-reload, treat the drop as a
gateway restart and show the same overlay with a new 'restart' reason
("Restarting chat…"). It reuses the existing extended retry budget +
the resolve-callback clear path (reason 'restart' keeps the visible
history, like a provider change, and shows no banner). Connection-driven
so it covers every restart cause without each one needing to fire an
event.
/simplify pass on the Telegram progress-toggle + chat reconnect-bar work:
- extract the duplicated reload progress-timer setInterval into a shared
  startReloadProgressTimer() helper (was byte-identical in onClose and the
  skill/provider event handler)
- narrow makeHandler's reason param back to 'skill' | 'provider' (the
  'restart' reason is set directly in onClose, never via makeHandler)
- flatten the keepHistoryReload branch in the hello-resolve callback by
  hoisting the provider banner out of the nested guard
- revert the Telegram streaming toggle to the captured prior value instead
  of assuming !next

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/ChatPopup.tsx (1)

832-855: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Fix: clear reloadingSkill when the extended retry budget is exhausted to avoid an endless “Restarting chat…” loop

In src/components/ChatPopup.tsx, onClose sets setStatus('error')/setErrorMsg(...) after retryCountRef.current >= maxRetries, but it never clears reloadingSkill. As a result, the error UI is gated behind status === 'error' && !reloadingSkill (so it never appears), and the safety-net effect (status === 'error' && reloadingSkill) keeps resetting retryCountRef and calling connect()—looping indefinitely while the gateway stays down.

🛠️ Proposed fix: surface the error after the restart retry budget is exhausted
       const maxRetries = skillInstalledRef.current ? SKILL_INSTALL_MAX_RETRIES : MAX_RETRIES
       if (retryCountRef.current < maxRetries) {
         retryCountRef.current++
         if (retryTimerRef.current) clearTimeout(retryTimerRef.current)
         retryTimerRef.current = setTimeout(() => connect(), RETRY_DELAY)
         return
       }
+      // Budget exhausted: drop the reload overlay so the error UI (and its
+      // manual "Try again") becomes reachable. Leaving reloadingSkill set
+      // would keep the status==='error' safety-net effect re-entering an
+      // endless reconnect loop against a gateway that is genuinely down.
+      if (skillInstalledRef.current) {
+        skillInstalledRef.current = false
+        reloadReasonRef.current = 'skill'
+        setReloadingSkill(false)
+      }
       setStatus('error')
       setErrorMsg('Could not connect to gateway')

This also applies to the skill/provider reload flows since they set reloadingSkill=true and share the same safety-net effect + error-panel gating.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ChatPopup.tsx` around lines 832 - 855, The retry-exhaustion
branch never clears the "reloading" flag, so the UI stays stuck in the
restarting loop; after you detect retryCountRef.current >= maxRetries (the
branch where you currently call setStatus('error') and setErrorMsg('Could not
connect to gateway')), also call setReloadingSkill(false) (and optionally reset
reloadProgress via setReloadProgress(0) and reloadReasonRef.current = null) so
the error panel can render and the retry safety-net stops; locate this change
beside the existing references to retryCountRef.current, maxRetries, setStatus,
and setErrorMsg in the onClose/reconnect handler.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/components/ChatPopup.tsx`:
- Around line 832-855: The retry-exhaustion branch never clears the "reloading"
flag, so the UI stays stuck in the restarting loop; after you detect
retryCountRef.current >= maxRetries (the branch where you currently call
setStatus('error') and setErrorMsg('Could not connect to gateway')), also call
setReloadingSkill(false) (and optionally reset reloadProgress via
setReloadProgress(0) and reloadReasonRef.current = null) so the error panel can
render and the retry safety-net stops; locate this change beside the existing
references to retryCountRef.current, maxRetries, setStatus, and setErrorMsg in
the onClose/reconnect handler.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6af702c3-7a64-49b4-bda7-aa61c9164305

📥 Commits

Reviewing files that changed from the base of the PR and between 9efb64e and 68ea665.

📒 Files selected for processing (2)
  • src/components/ChatPopup.tsx
  • src/components/SettingsApp.tsx

When the WS drops after a successful connection, onClose shows the reload
overlay and uses the extended retry budget. If the gateway never comes back,
retries exhaust and we set status='error' — but reloadingSkill stayed true, so
the overlay kept rendering (parked at 90%) on top of the error panel and the
chat looked stuck forever. Tear the overlay down, stop the progress timer, and
reset the reload flags in the exhaustion branch so the error panel renders and
a manual retry starts clean.

Addresses CodeRabbit review on PR #157.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/ChatPopup.tsx`:
- Around line 853-865: The connect() WS-config exhaustion path must perform the
same teardown as the onClose exhaustion branch: extract a helper (e.g.
tearDownReloadOverlay) defined before connect/startReloadProgressTimer that
clears reloadTimerRef (clearInterval + null), sets skillInstalledRef.current =
false, sets reloadReasonRef.current = 'skill', calls setReloadingSkill(false)
and setReloadProgress(0); then call this helper from the onClose exhaustion
branch (replacing the duplicated block) and from the catch/exhaustion branch
inside connect() so the error panel can render and the restart overlay cannot
get stuck.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d98a43f0-45ff-48c8-8803-51043e204764

📥 Commits

Reviewing files that changed from the base of the PR and between 68ea665 and c6a97e2.

📒 Files selected for processing (1)
  • src/components/ChatPopup.tsx

Comment thread src/components/ChatPopup.tsx Outdated
Follow-up to the CodeRabbit review on PR #157. The previous fix only cleared
the overlay in the onClose retry-exhaustion branch, but there's a sibling
exhaustion path in connect()'s ws-config catch. On a reboot where
/setup-api/gateway/ws-config keeps failing, retries exhaust there instead of in
onClose — and it only set status='error' without resetting reloadingSkill. The
error panel is gated on !reloadingSkill so it never rendered, and the safety-net
effect (error && reloadingSkill) reset the retry count and reconnected forever.

Extract a shared tearDownReloadOverlay() helper and call it from both
exhaustion paths.
@KrasimirKralev
KrasimirKralev merged commit 6e8c543 into beta May 29, 2026
7 checks passed
@KrasimirKralev
KrasimirKralev deleted the feat/telegram-progress-toggle branch June 3, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant