Suppress interim text echoes in Thinking cards - #2348
franksong2702 wants to merge 2 commits into
Conversation
Reading the diffWalked The live path tracks visible interim snippets and strips them from the Thinking card render: // static/messages.js (PR)
function _stripLiveVisibleAssistantEchoFromThinking(text, snippets){
let out=String(text||'');
(Array.isArray(snippets)?snippets:[]).forEach(snippet=>{
const visible=String(snippet||'').trim();
if(visible.length<20) return;
out=out.split(visible).join('');
});
return out.trim();
}And the settled path on // static/ui.js (PR)
function _stripVisibleAssistantEchoFromThinking(thinkingText, visibleText){
let out=String(thinkingText||'');
const visible=String(visibleText||'');
if(!out||!visible) return out.trim();
visible.split(/\n{2,}/).map(s=>s.trim()).filter(s=>s.length>=20).forEach(snippet=>{
out=out.split(snippet).join('');
});
return out.trim();
}Why the design holds up
Edge cases I'd flag, not block on
Test fidelity
VerdictApproach matches the issue scope and the established no-fuzzy-dedup direction. No blocking changes from me. |
|
Shipped in v0.51.72 ✅ Stage-365 release shipped at #2358. Merged commit a08dfff into master. Tag Verified before release:
Thanks for the contribution! Closing now. |
v0.51.72 — Release AV: - PR nesquena#2354 (fixes nesquena#2353) — Recovered pending turn context fix - PR nesquena#2348 (fixes nesquena#2346) — Thinking card interim-text echo suppression Hit Pitfall 6 again: contributor branches predated v0.51.70+71 so their CHANGELOG entries landed in pre-existing v0.51.68/69 sections after rebase. Manually moved entries to a new v0.51.72 section above v0.51.71, with proper PR # attribution (nesquena#2348 was 'PR TBD' on the contributor branch).
v0.51.73 — Release AW: - PR nesquena#2357 (fixes nesquena#2355) — Compression reference card anchoring fix The contributor's CHANGELOG entry was wiped during the --ours rebase conflict resolution (because v0.51.72 stamping moved Unreleased entries elsewhere). Added the canonical PR-numbered entry manually. Stage-366 carries only nesquena#2357. Two other recent PRs were deferred: - nesquena#2347 (live timeline restore, 380 LOC) — real semantic conflicts with v0.51.72's nesquena#2348 (_stripLiveVisibleAssistantEchoFromThinking + visibleInterimSnippets logic). Needs contributor rebase against current master. - nesquena#2356 (mobile panel touch targets, 98 LOC) — incompatible architectural conflicts in static/style.css (.sidebar-nav vs .rail layout choice). Needs contributor redesign against current sidebar-nav layout.
… 0.51.74) (#501) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/nesquena/hermes-webui](https://github.com/nesquena/hermes-webui) | patch | `0.51.67` → `0.51.74` | --- ### Release Notes <details> <summary>nesquena/hermes-webui (ghcr.io/nesquena/hermes-webui)</summary> ### [`v0.51.74`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05174--2026-05-16--Release-AX-stage-367--4-PR-safe-lane-batch--2362-table-cell-spacing--2363-run-state-consistency-RFC--2365-customproviders-list-format--2367-settings-sidebar-i18n) [Compare Source](nesquena/hermes-webui@v0.51.73...v0.51.74) ##### Added - **PR [#​2363](nesquena/hermes-webui#2363 by [@​franksong2702](https://github.com/franksong2702) (refs [#​2361](nesquena/hermes-webui#2361), refs [#​1925](nesquena/hermes-webui#1925)) — Adds `docs/rfcs/webui-run-state-consistency-contract.md` as a documentation companion to the [#​1925](nesquena/hermes-webui#1925) runtime-boundary RFC. Documents the shared coherence contract across visible transcript, model context, pending turn metadata, live stream, run journal, compression handoff, browser timeline cache, and sidebar metadata. Complementary to [#​1925](nesquena/hermes-webui#1925): that RFC says where execution ownership should move, this one says what must stay coherent across the current and future state layers. ##### Fixed - **PR [#​2362](nesquena/hermes-webui#2362 by [@​franksong2702](https://github.com/franksong2702) (fixes [#​2360](nesquena/hermes-webui#2360)) — Markdown table rows no longer become too tall when cell text is wrapped in paragraph tags by the renderer. Adds a table-specific CSS reset for `.msg-body td p` and `.msg-body th p` so the global `margin-bottom: 10px` rule on `.msg-body p` doesn't add unwanted vertical space inside table cells. Especially visible on narrow viewports such as iPad Safari/Chrome. - **PR [#​2365](nesquena/hermes-webui#2365 by [@​mccxj](https://github.com/mccxj) (fixes [#​1106](nesquena/hermes-webui#1106)) — `get_available_models()` now handles YAML-list format `custom_providers.models` entries in addition to dict format. Pre-fix, declaring models as a list (`[m1, m2]`) or list-of-dicts (`[{id: m1, label: ...}]`) in `config.yaml` silently discarded every model from that provider in the picker dropdown because the code only recognized dict shape (`{model_id: {}}`). Now supports all three YAML shapes consistently with existing provider-config and live-models-fallback handlers. - **PR [#​2367](nesquena/hermes-webui#2367 by [@​mccxj](https://github.com/mccxj) — Settings sidebar menu items (Conversation, Appearance, Preferences, Plugins, System) now respect locale selection. Pre-fix these were hardcoded English; only Providers had `data-i18n`. Adds `data-i18n` attributes plus the missing `settings_tab_plugins` key. **Stage-367 maintainer fix applied inline**: the PR only added the new key to English, breaking 5 locale-parity tests. Added `settings_tab_plugins` translations to all 10 non-English locales (it/ja/ru/es/de/zh/zh-TW/pt/ko/fr). ### [`v0.51.73`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05173--2026-05-16--Release-AW-stage-366--1-PR-safe-lane-batch--2357-compression-reference-card-anchoring-fix) [Compare Source](nesquena/hermes-webui@v0.51.72...v0.51.73) ##### Fixed - **PR [#​2357](nesquena/hermes-webui#2357 by [@​franksong2702](https://github.com/franksong2702) (fixes [#​2355](nesquena/hermes-webui#2355)) — Auto-compression reference cards no longer get mixed into the final answer turn after a session rotation. Pre-fix, `_insertCompressionLikeNodeByRawIdx()` appended the compression-reference node to the future assistant anchor turn's blocks, which projected the `[CONTEXT COMPACTION — REFERENCE ONLY]` card into the live tail. The fix inserts the node *before* the anchor segment so the reference card stays a sibling, not a child of the answer turn. ### [`v0.51.72`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05172--2026-05-16--Release-AV-stage-365--2-PR-safe-lane-batch--2354-recovered-pending-turn-context-fix--2348-Thinking-card-interim-text-echo-suppression) [Compare Source](nesquena/hermes-webui@v0.51.71...v0.51.72) ##### Fixed - **PR [#​2354](nesquena/hermes-webui#2354 by [@​franksong2702](https://github.com/franksong2702) (fixes [#​2353](nesquena/hermes-webui#2353)) — Stale stream recovery now keeps a recovered pending user turn in the model context (`context_messages`) as well as the visible transcript. Pre-fix, a server restart during an in-flight turn could restore the user's message in WebUI while omitting it from `context_messages`, so the next agent turn could forget a prompt that was visibly present just above it. The repair path now appends the recovered user turn to both surfaces with 8-message lookback dedup so already-checkpointed entries are not duplicated. - **PR [#​2348](nesquena/hermes-webui#2348 by [@​franksong2702](https://github.com/franksong2702) (fixes [#​2346](nesquena/hermes-webui#2346)) — Thinking cards now suppress exact snippets that are already shown as user-visible interim assistant text, avoiding duplicated progress lines when an agent emits the same sentence through both reasoning and interim-assistant callbacks. Tracks `_liveThinkingText` during the live stream to strip the visible echo from the live Thinking card display; applies the same suppression in the settled-transcript path so reload/session-switch sees the cleaned-up view too. ### [`v0.51.71`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05171--2026-05-16--Release-AU-stage-364--3-PR-batch--2349-stale-stream-cleanup-non-touching--2343-profiles-vs-workspaces-help-card--2283-run-event-journal-replay-refs-1925-RFC-slice-1--with-Opus-caught-replay-double-render-fix) [Compare Source](nesquena/hermes-webui@v0.51.70...v0.51.71) ##### Added - **PR [#​2343](nesquena/hermes-webui#2343 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​2147](nesquena/hermes-webui#2147)) — The Profiles panel now includes an inline "Profiles vs workspaces" explainer. The copy clarifies that profiles control how the agent works — identity, memory, skills, model/provider config, and tools — while workspaces control what project/files a session operates on, making the OpenClaw-style role/profile mental model easier to map onto Hermes WebUI. - **PR [#​2283](nesquena/hermes-webui#2283 by [@​franksong2702](https://github.com/franksong2702) (refs [#​1925](nesquena/hermes-webui#1925)) — Adds an append-only WebUI run event journal for browser-originated chat streams (refs [#​1925](nesquena/hermes-webui#1925)). Every SSE event emitted by the legacy in-process runner is mirrored to a per-session JSONL file, `/api/chat/stream/status` reports when replay is available for a dead stream, `/api/chat/stream` can replay journaled events with SSE event IDs and a clear stale-restart diagnostic, and the frontend reattach path uses that replay before clearing local running state. Reconnect replay uses the last rendered SSE event id as its `after_seq` cursor so it does not replay already-rendered events, and journal fsync defaults to terminal events only (`HERMES_WEBUI_RUN_JOURNAL_FSYNC=eager` restores per-event fsync). This is the first compatibility slice only: it preserves the existing WebUI runner and does not make active execution survive a WebUI restart. **Stage-364 maintainer fix applied inline**: Opus advisor caught that live SSE frames emitted by `_sse()` in `api/streaming.py:2296` carry no `id:` field, so the frontend's `_lastRunJournalSeq` cursor stayed at 0 during live streaming and a mid-stream error→replay would arrive with `after_seq=0`, replaying every journaled event from seq 1 and double-rendering tokens. The fix adds `STREAM_LAST_EVENT_ID: dict = {}` as a per-stream side-channel in `api/config.py`; `put()` writes the journal's `event_id` to that dict on every event; `_handle_sse_stream` reads it at SSE emit time and uses `_sse_with_id(handler, event, data, event_id)` when present. The queue tuple shape is preserved as `(event, data)` so existing queue consumers (cancel sentinel, sprint42/51 tests, etc.) are not broken. Cleaned up in the worker's finally block alongside the other STREAM\_\* dicts. 6 regression tests added covering side-channel dict declaration, writer/reader paths, tuple shape preservation, and cleanup. ##### Fixed - **PR [#​2349](nesquena/hermes-webui#2349 by [@​franksong2702](https://github.com/franksong2702) (fixes [#​2345](nesquena/hermes-webui#2345)) — Clearing stale stream runtime flags no longer refreshes a session's `updated_at`, so old compressed continuations should not jump back to the top of the sidebar just because WebUI repaired a dead `active_stream_id` during a read/list request. ### [`v0.51.70`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05170--2026-05-16--Release-AS-stage-363--4-PR-snapshotjournalUI-batch--2337-compression-snapshot-runtime-clear--2334-turn-journal-fcntl-lock--2342-INFLIGHT-reattach-pending-row--2339-workspace-panel-edge-toggle) [Compare Source](nesquena/hermes-webui@v0.51.69...v0.51.70) ##### Added - **PR [#​2339](nesquena/hermes-webui#2339 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​2211](nesquena/hermes-webui#2211)) — The workspace panel now has a small desktop edge toggle that remains clickable after the right panel is hidden, making it possible to reopen the workspace browser without returning to Settings. The existing panel close button and composer workspace button remain unchanged; the new affordance only appears when the workspace panel is closed on desktop widths. ##### Fixed - **PR [#​2337](nesquena/hermes-webui#2337 by [@​Michaelyklam](https://github.com/Michaelyklam) (closes [#​2336](nesquena/hermes-webui#2336)) — Pre-compression snapshot preservation now also clears stale runtime stream fields when the existing on-disk snapshot is already as complete as the in-memory session. This keeps the load-and-mark branch aligned with the full-save branch and adds regression coverage so archived parent snapshots cannot retain stale `active_stream_id` / `pending_*` state. - **PR [#​2342](nesquena/hermes-webui#2342 by [@​franksong2702](https://github.com/franksong2702) (fixes [#​2341](nesquena/hermes-webui#2341)) — Reattaching to an active streaming session now keeps the user prompt that started the running turn visible. Pre-fix, reload/session-switch restore could hydrate from the browser's INFLIGHT stream cache while the backend still held the initiating prompt only as `pending_user_message`, so the transcript showed assistant Thinking/Tool activity without the user's just-submitted message. The restore path now merges that pending user row into the live transcript before rendering and updates the INFLIGHT cache, while duplicate suppression checks the current message array so final session payloads do not show the prompt twice. - **PR [#​2334](nesquena/hermes-webui#2334 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​2097](nesquena/hermes-webui#2097)) — Turn journal appends now take an advisory `flock` around each JSONL event write and fsync when Unix file locks are available. This keeps oversized submitted-message events from interleaving at the byte level if a future deployment runs multiple WebUI worker processes against the same state directory, while preserving the previous best-effort append path on platforms without `fcntl`. ### [`v0.51.69`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05169--2026-05-15--Release-AT-stage-362--8-PR-follow-up-batch--Ollama-routing--legacy-toolset--cancel-copy--cleanup--custom-provider-mismatch--cron-metadata--dead-code-removal-2323-reverted-after-Opus-caught-silent-regression-refiled-as-2321-reopen) [Compare Source](nesquena/hermes-webui@v0.51.68...v0.51.69) ##### Added - **PR [#​2332](nesquena/hermes-webui#2332 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​2290](nesquena/hermes-webui#2290)) — Cron run history/output cards now surface token/cost metadata when the underlying cron output markdown includes it. The backend parses optional model/token/cost/duration frontmatter from cron output files and returns it from `/api/crons/history` and `/api/crons/run`; the Tasks panel renders a compact usage strip beside run rows and below expanded output without affecting older outputs that lack usage metadata. ##### Fixed - **PR [#​2322](nesquena/hermes-webui#2322 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​2271](nesquena/hermes-webui#2271)) — LAN Ollama models selected from endpoint-discovered `custom:<host>-<port>` / `custom:<host>:<port>` picker entries now route through the configured `ollama` provider and base URL instead of surfacing a missing `CUSTOM_*_API_KEY` error. The picker still surfaces endpoint-discovered entries; the fix is to recognize them as UI routing hints matching the configured local-server base URL and resolve them via the actual `ollama` provider. - **PR [#​2326](nesquena/hermes-webui#2326 by [@​Michaelyklam](https://github.com/Michaelyklam) (closes [#​2232](nesquena/hermes-webui#2232)) — Legacy `hermes` CLI toolset alias is now normalized to `hermes-cli` + `hermes-api-server` when WebUI resolves CLI toolsets from shared Hermes config. Modern Hermes Agent exposes the composite under those two names; older configs that still contain the legacy `hermes` toolset name no longer surface as "unknown toolset" warnings. - **PR [#​2327](nesquena/hermes-webui#2327 by [@​dotBeeps](https://github.com/dotBeeps) — Cancel-mid-stream messaging now uses the user's configured assistant name (e.g. "Hermes") instead of hardcoded "Skyly". Preferences allow defining an Assistant Name that persists throughout the UI; the cancel copy was the last place still showing the persona placeholder. Backend persisted-cancelled-turn text and frontend live-cancel toast both now read from the same `botName` setting. - **PR [#​2328](nesquena/hermes-webui#2328 by [@​Michaelyklam](https://github.com/Michaelyklam) (closes [#​2325](nesquena/hermes-webui#2325)) — Two cleanup follow-ups from v0.51.68 stage-361 review: (a) when a session is deleted via `/api/session/delete`, its `~/.hermes/webui/attachments/<sid>/` inbox is also removed (orphan accumulation prevention); (b) the deferred stream-recovery listener bound by `_deferStreamErrorIfPageHidden()` now bails out when the user switches sessions in the same tab — the recovery would otherwise fire `setComposerStatus('Reconnected')` for a stream the user has moved past. Both fixes are narrow cleanup with regression tests. - **PR [#​2330](nesquena/hermes-webui#2330 by [@​Michaelyklam](https://github.com/Michaelyklam) (closes [#​2329](nesquena/hermes-webui#2329)) — Provider mismatch warnings now skip named custom providers such as `custom:zenmux`. Custom aggregators can legitimately route vendor-prefixed models like `google/gemini-3.1-flash-lite`, so `_checkProviderMismatch()` now treats `custom:<name>` the same as bare `custom` and avoids false-positive "may not work with your configured provider" warnings. - **PR [#​2331](nesquena/hermes-webui#2331 by [@​Michaelyklam](https://github.com/Michaelyklam) — Live activity row now shows a transient human-readable progress phrase derived from the current tool category (e.g. "Reading file…", "Searching files…", "Running command…") instead of only the elapsed-time counter `Working 1m 23s`. Compact transcript view unchanged. - **PR [#​2333](nesquena/hermes-webui#2333 by [@​Michaelyklam](https://github.com/Michaelyklam) (closes [#​2312](nesquena/hermes-webui#2312) follow-up [#​1](nesquena/hermes-webui#1)) — Removed dead production helper `_save_pre_compression_snapshot()` at `api/streaming.py:1945`. The production path now uses `_preserve_pre_compression_snapshot()` exclusively (which must index snapshots with `skip_index=False` for sidebar filtering). The dead helper was only called from `tests/test_compression_snapshot_runtime_clear.py`; the test is retargeted to exercise the actual production helper instead. Closes follow-up item [#​1](nesquena/hermes-webui#1) from the v0.51.66 review ([#​2312](nesquena/hermes-webui#2312)). ### [`v0.51.68`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05168--2026-05-15--Release-AR-stage-361--4-PR-follow-up-batch--2315-profile-skill-seeding--2317-theme-fallback--2318-mobile-stream-defer--2319-chat-upload-relocation--with-Opus-caught-vision-model-regression-fix) [Compare Source](nesquena/hermes-webui@v0.51.67...v0.51.68) ##### Added - **PR [#​2319](nesquena/hermes-webui#2319 by [@​Michaelyklam](https://github.com/Michaelyklam) — Chat file uploads now land in a session-scoped attachment inbox instead of cluttering the active workspace root. By default uploads are stored under `~/.hermes/webui/attachments/<session_id>/`; operators can override the root with `HERMES_WEBUI_ATTACHMENT_DIR`, and the agent still receives the absolute uploaded file path for context. Archive extraction stays workspace-scoped (it's an explicit workspace operation). README updated to document the new default location. **Stage-361 maintainer fix applied inline**: Opus advisor caught that `_build_native_multimodal_message` at `api/streaming.py:787` required uploads to be under `workspace_root`, which would have silently dropped every image upload for vision-capable models once the inbox moved outside the workspace. The fix adds `_attachment_root()` (from `api/upload.py`) as a second allowed location, with 3 regression tests covering the new code path AND verifying the original workspace + cross-root rejection paths still work. ##### Fixed - **PR [#​2315](nesquena/hermes-webui#2315 by [@​Michaelyklam](https://github.com/Michaelyklam) (closes [#​2305](nesquena/hermes-webui#2305), refs [#​749](nesquena/hermes-webui#749)) — WebUI profile creation now seeds bundled profile skills for newly-created non-cloned profiles, matching the CLI's `hermes profile create` behaviour. Pre-fix, creating a profile via Settings → New Profile (without checking "Clone from active profile") left the profile's `skills/` directory empty, which was inconsistent with CLI-created profiles that get the full bundled-skills overlay. The fix calls `seed_profile_skills(profile_path, quiet=True)` after `profile_path.mkdir()` when `clone_from is None`. Cloned profiles still inherit skills from their source — they don't get a second bundled-skills overlay. Seed failures (e.g. `hermes_cli` unavailable in Docker fallback) are logged as warnings, not fatal — profile creation still succeeds. - **PR [#​2317](nesquena/hermes-webui#2317 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​2312](nesquena/hermes-webui#2312) follow-up [#​2](nesquena/hermes-webui#2)) — Appearance boot reconciliation now treats explicit `light`, `dark`, and `system` localStorage theme values as user selections when a prior Settings autosave failed. Pre-fix, the predicate `lsHasExplicitTheme = lsTheme === 'system'` only treated 'system' as explicit, so a user who picked `light` on a server defaulted to `dark` (or vice versa) with a failed autosave still reverted to the server default on refresh. Now broadened to `['system','light','dark'].includes(lsTheme)`. Skin handling was already correct (`lsSkin !== 'default'`). Closes follow-up item [#​2](nesquena/hermes-webui#2) from the v0.51.66 review ([#​2312](nesquena/hermes-webui#2312)). - **PR [#​2318](nesquena/hermes-webui#2318 by [@​Michaelyklam](https://github.com/Michaelyklam) (closes [#​2307](nesquena/hermes-webui#2307)) — Mobile/Android backgrounded tabs no longer show a permanent `**Error:** Connection lost` banner when the backend stream is still alive and able to replay buffered events. Pre-fix, the SSE error finalization fired regardless of page visibility state, so any tab discarded by the mobile OS (battery saver, tab compression, brief switch to another app) showed a permanent error even though the stream could be re-attached on visibility return. The fix defers inline stream error rendering while `document.visibilityState === 'hidden'` or `document.wasDiscarded === true`, then on visibility return polls `/api/chat/stream/status?stream_id=...`. If the stream is still active, reattaches with a fresh `EventSource`. If not, falls back to the settled-session restore path. If both paths fail, falls back to the original error rendering. Behaviour on desktop and on tabs that ARE visible is unchanged. </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19--> Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/501
v0.51.72 — Release AV: - PR nesquena#2354 (fixes nesquena#2353) — Recovered pending turn context fix - PR nesquena#2348 (fixes nesquena#2346) — Thinking card interim-text echo suppression Hit Pitfall 6 again: contributor branches predated v0.51.70+71 so their CHANGELOG entries landed in pre-existing v0.51.68/69 sections after rebase. Manually moved entries to a new v0.51.72 section above v0.51.71, with proper PR # attribution (nesquena#2348 was 'PR TBD' on the contributor branch).
v0.51.73 — Release AW: - PR nesquena#2357 (fixes nesquena#2355) — Compression reference card anchoring fix The contributor's CHANGELOG entry was wiped during the --ours rebase conflict resolution (because v0.51.72 stamping moved Unreleased entries elsewhere). Added the canonical PR-numbered entry manually. Stage-366 carries only nesquena#2357. Two other recent PRs were deferred: - nesquena#2347 (live timeline restore, 380 LOC) — real semantic conflicts with v0.51.72's nesquena#2348 (_stripLiveVisibleAssistantEchoFromThinking + visibleInterimSnippets logic). Needs contributor rebase against current master. - nesquena#2356 (mobile panel touch targets, 98 LOC) — incompatible architectural conflicts in static/style.css (.sidebar-nav vs .rail layout choice). Needs contributor redesign against current sidebar-nav layout.
v0.51.72 — Release AV: - PR nesquena#2354 (fixes nesquena#2353) — Recovered pending turn context fix - PR nesquena#2348 (fixes nesquena#2346) — Thinking card interim-text echo suppression Hit Pitfall 6 again: contributor branches predated v0.51.70+71 so their CHANGELOG entries landed in pre-existing v0.51.68/69 sections after rebase. Manually moved entries to a new v0.51.72 section above v0.51.71, with proper PR # attribution (nesquena#2348 was 'PR TBD' on the contributor branch).
v0.51.73 — Release AW: - PR nesquena#2357 (fixes nesquena#2355) — Compression reference card anchoring fix The contributor's CHANGELOG entry was wiped during the --ours rebase conflict resolution (because v0.51.72 stamping moved Unreleased entries elsewhere). Added the canonical PR-numbered entry manually. Stage-366 carries only nesquena#2357. Two other recent PRs were deferred: - nesquena#2347 (live timeline restore, 380 LOC) — real semantic conflicts with v0.51.72's nesquena#2348 (_stripLiveVisibleAssistantEchoFromThinking + visibleInterimSnippets logic). Needs contributor rebase against current master. - nesquena#2356 (mobile panel touch targets, 98 LOC) — incompatible architectural conflicts in static/style.css (.sidebar-nav vs .rail layout choice). Needs contributor redesign against current sidebar-nav layout.
Thinking Path
Fixes #2346.
The duplicated text is not primarily a backend streaming bug. The model can emit the same user-visible progress sentence through both reasoning and
interim_assistant. The UI should preserve the visible assistant text, but the Thinking card should not mirror the exact same snippet immediately above it.This PR keeps the existing Thinking card semantics: the card remains available and expandable, while visible interim assistant text remains in the transcript. The display cleanup happens only when the same snippet is already visible to the user.
What Changed
Why It Matters
The user should see a readable timeline, not the same progress sentence twice in adjacent surfaces. Keeping the visible interim text and suppressing only the duplicate inside Thinking preserves the useful part of both channels.
Verification
node --check static/messages.js && node --check static/ui.jsuv run --python 3.12 --with pytest --with pyyaml pytest tests/test_ui_tool_call_cleanup.py tests/test_regressions.py tests/test_reasoning_show_hide.py98 passed, 1 skippedRisks / Follow-ups
Model Used
Codex GPT-5. AI assisted with investigation, implementation, tests, and PR text.