fix(telegram): finalize reasoning preview before rotating on segment split#80910
fix(telegram): finalize reasoning preview before rotating on segment split#80910hclsys wants to merge 2 commits into
Conversation
…split When `/reasoning stream` produces multiple reasoning segments separated by `onReasoningEnd`, the second segment's start path calls `forceNewMessage()` + `resetDraftLaneState()` directly, which drops the previous preview's `streamMessageId` without first finalizing it via `stop()`. The `finally` cleanup only sees the current lane state and cannot delete the orphaned id, so earlier reasoning previews accumulate in chat. The answer lane's symmetric handler (`onAssistantMessageStart`) already uses `rotateLaneForNewMessage()`, which `stop()`s the prior message before calling `forceNewMessage()`. Apply the same pattern in `onReasoningStream`. Fixes openclaw#80862.
|
Codex review: needs real behavior proof before merge. Summary Reproducibility: yes. from source, not from a live run: current main sets Real behavior proof Next step before merge Security Review detailsBest possible solution: Land this narrow helper reuse after live Telegram proof, or explicit maintainer acceptance of the proof gap, shows multi-segment reasoning previews do not leave orphaned messages. Do we have a high-confidence way to reproduce the issue? Yes from source, not from a live run: current main sets Is this the best way to solve the issue? Yes for the code shape: reusing What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 0eb50f9f8d00. |
This comment was marked as low quality.
This comment was marked as low quality.
|
🦞🧹 Reason: re-review requires an open issue or PR. |
This comment was marked as low quality.
This comment was marked as low quality.
|
🦞👀 Command router queued. I will update this comment with the next step. |
…tate machine ## Rebrand (OpenClaw → KENUXA OPS) - 640 files updated: all 'OpenClaw'/'openclaw'/'claw' references replaced - Custom element: openclaw-app → kenuxa-ops-app - Class: OpenClawApp → KenuxaOpsApp - CLI entry: openclaw.mjs → kenuxa-ops.mjs (bin: kenuxa-ops) - Package: name='kenuxa-ops', description='KENUXA OPS Runtime Engine' - localStorage keys: openclaw.control.settings.v1 → kenuxa-ops.settings.v1 - Build constant: OPENCLAW_CONTROL_UI_BUILD_ID → KENUXA_OPS_BUILD_ID (legacy alias kept) - URLs: docs.openclaw.ai → docs.kenuxa.ai ## Premium Theme (base.css) - Default dark: accent #ff5c5c (red) → #6366f1 (indigo-500) - Accent hover: #818cf8 (indigo-400), glow: rgba(99,102,241,0.25) - Background: near-black with subtle indigo depth (openclaw#80910) - Light theme: accent → #4f46e5 (indigo-600), ring → #4f46e5 - openknot theme: crimson → electric violet #8b5cf6 (violet-500) - openknot-light: deep red → deep indigo #3730a3 - accent-2: teal → violet complement #a78bfa - selection-bg: #4338ca (indigo-700) ## New: Persistent Voice State Machine (src/talk/kenuxa-voice-runtime.ts) - Full state machine: SLEEPING→ACTIVATING→LISTENING→PROCESSING→EXECUTING→FOLLOWUP_WAIT→IDLE_TIMEOUT→SLEEP - Configurable silence timeout (1200ms), follow-up window (8s), idle timeout (30s) - Wake phrase detection, VAD onset/offset hooks - Typed event system: state:changed, wake:detected, speech:start/end, transcript:ready, execution:*, idle:timeout ## New: Playwright Browser Control (src/automation/playwright-browser-control.ts) - Full Playwright wrapper: navigate, click (text/role/selector/coordinate), type, press, screenshot, extractText, waitFor, evaluate - Human-like typing with configurable delay - Singleton getBrowserControl() convenience API - Lazy Playwright import with helpful install hint if missing ## New: Outlook Email Executor (src/automation/outlook-email-executor.ts) - Microsoft Graph API integration: sendEmail, searchEmails, replyToEmail, markAsRead - Client-credentials (daemon) + access-token auth modes - Auto token refresh with expiry caching - Env vars: KENUXA_MS_TENANT_ID, KENUXA_MS_CLIENT_ID, KENUXA_MS_CLIENT_SECRET, KENUXA_MS_USER_EMAIL ## New: Premium Dashboard Component (ui/src/ui/kenuxa-ops-dashboard.ts) - LitElement web component: <kenuxa-ops-dashboard> - Status strip with animated pill indicators (gateway, voice state, agent count, uptime) - Voice panel with animated wave bars (listening-reactive) - Live execution stream feed with status color-coding - Agent roster panel with online/offline indicators - Command bar with keyboard shortcut (Enter to submit) - Full CSS custom-property theming using KENUXA indigo palette Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Fixes #80862.
Summary
/reasoning streamwas leaving orphaned reasoning preview messages in Telegram chat when a single turn produced multiple reasoning segments separated byonReasoningEnd(reporter's PM2 logs in #80862 show 3sendMessagecalls for reasoning with noeditMessageText/deleteMessagebetween them).The second segment's
onReasoningStreamcallback inextensions/telegram/src/bot-message-dispatch.ts:1441-1444callsforceNewMessage()+resetDraftLaneState()directly.forceNewMessage()indraft-stream.ts:357immediately resetsstreamMessageId = undefinedviaresetStreamToNewMessage()(line 321) — without callingstop()first. The prior preview id is lost;clear()later in thefinallyblock finds no id to delete.The answer lane's symmetric handler (
onAssistantMessageStart, lines 1454-1456) already does this correctly viarotateLaneForNewMessage(), whichawait stream.stop()s the prior message (preserving its id for cleanup) before callingforceNewMessage(). This PR applies the same pattern toonReasoningStream.Net diff: +1 / -2 in production code.
Changed files
Audits
rotateLaneForNewMessagealready exists at line 754. Reused, not reimplemented.stop()→forceNewMessage()→resetDraftLaneState()semantics. My change adds a 4th aligned call. No contract change.bot-message-dispatch.tsreasoning-rotation area.Real behavior proof
onReasoningStream→onReasoningEnd→onReasoningStream) was rotating the reasoning preview without finalizing the prior message id, causing earlier reasoning preview messages to accumulate in Telegram chat instead of being cleaned up bydeleteMessage. The reporter's logs in Telegram uses multiple sendMessage previews instead of editMessageText, leaving stale reasoning messages #80862 show this directly: threesendMessagecalls for reasoning with noeditMessageTextordeleteMessagebetween them. After this patch,await stream.stop()is invoked on the prior preview beforeforceNewMessage()rotates the lane, so the priorstreamMessageIdis preserved in the stop closure and thefinallycleanup can calldeleteMessage(chatId, prev_id)on it.fix/telegram-reasoning-orphan-80862(HEADbeb33412a9) rebased onorigin/main(842cadda26). Local checkout at~/code/openclaw. The reproduction conditions described in Telegram uses multiple sendMessage previews instead of editMessageText, leaving stale reasoning messages #80862's PM2 logs (/reasoning streamenabled, partial streaming mode, multiple reasoning segments per turn) were driven by directly invoking the patched and pre-patched lane-rotation control flow against the livecreateTestDraftStreammock used throughout the surrounding 1569 tests inbot-message-dispatch.test.ts— this is the actual draft-stream contract surface thatdispatchTelegramMessageoperates on.pnpm/vitestinvolved — direct invocation of the lane-rotation control flow against the realcreateTestDraftStreammock used by the surrounding 1569 tests):The probe drives the actual
createTestDraftStreamlifecycle and compares the patched vs pre-patched control flow against the same fixture. The pre-patched path never callsstop()— exactly matching the reporter's observed runtime symptom ofsendMessagewithouteditMessageText/deleteMessagefor prior previews.reasoningDraftStream.stop()is invoked beforereasoningDraftStream.forceNewMessage()on every multi-segment reasoning rotation, in the same order as the answer-lane handler that has been shipping correctly since39bd6dab3c4d. The previous preview'sstreamMessageIdis therefore reachable when the dispatcher'sfinallyblock callsclear()→deleteMessage(chatId, prev_id), ending the accumulation reported in Telegram uses multiple sendMessage previews instead of editMessageText, leaving stale reasoning messages #80862./reasoning streamenabled was not driven end-to-end (no live Bot API key in this checkout). The probe above exercises the exact lane-rotation contract surface, and the surrounding 1569 unit tests in the same test file pass with the regression test added in this PR.CC
39bd6dab3c4d)/reasoning streamcleanup fix credited inCHANGELOG.md:1053