feat(telegram): Settings toggle to disable bot progress streaming - #157
Conversation
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).
|
Warning Review limit reached
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 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds 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. ChangesTelegram Progress Streaming Feature
ChatPopup reconnect & reload UX
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
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
There was a problem hiding this comment.
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 liftFix: clear
reloadingSkillwhen the extended retry budget is exhausted to avoid an endless “Restarting chat…” loopIn
src/components/ChatPopup.tsx,onClosesetssetStatus('error')/setErrorMsg(...)afterretryCountRef.current >= maxRetries, but it never clearsreloadingSkill. As a result, the error UI is gated behindstatus === 'error' && !reloadingSkill(so it never appears), and the safety-net effect (status === 'error' && reloadingSkill) keeps resettingretryCountRefand callingconnect()—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=trueand 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
📒 Files selected for processing (2)
src/components/ChatPopup.tsxsrc/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.
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
src/components/ChatPopup.tsx
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.
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.ts—getTelegramProgressStreaming()/setTelegramProgressStreaming(enabled). OpenClaw gates the progress draft on the channel's streaming mode, so OFF writeschannels.telegram.streaming = { mode: "off" }and ON removes the override (restoring OpenClaw's default). Spreads the existing telegram object sobotToken/enabledsurvive, and never writesdmPolicy/allowFrom(same security invariant assetTelegramToken)./setup-api/telegram/streaming(new) — GET reads the current state, POST writes it +restartGateway(). Returns 502 "saved but restart failed" (mirroringai-models/configure) so a restart hiccup doesn't read as a save failure.SettingsApp.tsx— arole="switch"toggle in the Telegram connected card (only meaningful with a bot connected), optimistic with a pending spinner since the POST restarts the gateway.settings.telegramProgress+settings.telegramProgressHintin all 10 locales.Chat reconnect overlay
ChatPopup.tsx— the WSonClosenow trips the reload overlay (reason'restart') whenever the socket drops after a successful connection (connectedOnceRef), guarded against re-tripping mid-reconnect. Thehello-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
setInterval(duplicated inonCloseand the skill/provider handler) into one sharedstartReloadProgressTimer().makeHandler's param to'skill' | 'provider'(the'restart'reason is set directly inonClose).keepHistoryReloadbranch in thehello-resolve callback.!next.Safety:
gateway-pre-start.sh+ config validatorgateway-pre-start.shonly stripsdmPolicy/allowFromon restart — it does not touch astreamingkey, so the user's choice persists across reboots. (Confirmed.)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 earliersupportedReasoningEffortsbrick).Test plan
mode:offpreserving 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 JetsonSummary by CodeRabbit
New Features
Improvements