fix: bound oversized paginated message content - #6815
Andre-4711 wants to merge 3 commits into
Conversation
|
| Filename | Overview |
|---|---|
| api/routes.py | Adds bounded paginated previews and applies existing display-window semantics to synthesized sessions. |
| static/sessions.js | Extends authoritative full-history loading to detect clipped-content markers. |
| static/ui.js | Reloads authoritative content before edit and regenerate operations. |
| tests/test_limited_session_content_actions.py | Adds focused Node-based regression coverage for full-content reloads before mutations. |
| tests/test_session_tail_payload.py | Covers payload bounds, structured content semantics, source immutability, and synthesized-session behavior. |
Sequence Diagram
sequenceDiagram
participant UI as Browser UI
participant API as GET /api/session
participant Store as Session storage
UI->>API: Paginated request with msg_limit
API->>Store: Load authoritative transcript
Store-->>API: Full stored messages
API->>API: Select display window
API->>API: Bound oversized renderable rows
API-->>UI: Paginated previews with truncation markers
UI->>API: Full-history request before mutation
API->>Store: Load authoritative transcript
Store-->>API: Complete messages
API-->>UI: Exact full history
Reviews (2): Last reviewed commit: "fix: preserve visible structured preview..." | Re-trigger Greptile
f8f6831 to
cfa45ff
Compare
|
Thanks @Andre-4711 — this is a real and valuable fix (a ~5.38M-char row bricking native WKWebView on "Loading conversation…" is a genuine brick), and the backend bounding is well-built: Codex verified exact-limit passthrough, limit+1 landing at exactly 65,536, safe handling of empty/ I gated the current head (Codex adversarial review + full suite). Holding for a revision — there are 4 issues, two verified by Codex on the frontend and two real regressions the suite caught: 1. CORE regression — Claude-Code profile-agnostic detail-load returns 0 messages (
|
nesquena-hermes
left a comment
There was a problem hiding this comment.
Thanks @Andre-4711 — bounding oversized paginated message content is a real need, and the core byte-math is correct (persisted messages are never mutated, no-msg_limit retrieval stays canonical, exactly 65,536 chars unbounded while 65,537 reduces to 65,536, ordinary/synthesized pagination agree, 26 focused tests pass). But the gate found this is the same defect class as #7040 — the bounded preview leaks into the canonical-action paths, plus a render-safety cut bug. Seven to fix:
Must-fix (CORE) — oversized messages aren't recoverable in the transcript UI
static/ui.js:16935. The renderer creates no expand/full-content control for _content_truncated; the only marker consumers are the loader and edit path, and session-jump buttons are disabled by default. Add a per-message full-content action that fetches the no-msg_limit canonical value and displays/downloads it.
Must-fix (SILENT) — Copy copies the 64 KiB preview + truncation notice
static/ui.js:8831. copyMsg() reads only stale data-raw-text and never triggers authoritative loading. Capture session ID + absolute message index, load canonical content when marked (guard session switches), and copy the full normalized message.
Must-fix (SILENT) — Listen/TTS speaks only the preview + truncation notice
static/ui.js:9009. speakMessage() has the same stale data-raw-text dependency. Resolve the full message via the same race-safe absolute-index helper before stripping Markdown and starting TTS.
Must-fix (SILENT) — Markdown download exports bounded previews
static/boot.js:2101. transcript() serializes current S.messages (unlike JSON/HTML exports, which use the canonical server endpoint), so the Markdown contains the preview and omits the full value. Await authoritative loading with a session guard before building the blob, or add a canonical server-side Markdown export.
Must-fix (CORE render-safety) — slicing can create DOM-breaking bounded content
api/routes.py:8895. Both string and structured-text paths cut without respecting HTML comments, safe tags, fences, or tables. Verified: a valid cutoff-crossing <!-- … --> becomes an unclosed <!-- in actual renderMd() output → the HTML tokenizer eats the footer/actions. Build previews at safe structural boundaries, or neutralize unmatched comments/tags before rendering. Cover a cutoff-crossing actual-renderMd() case.
Also
static/ui.js:19165— preserve normalized edit text for ordinary messages and normalize authoritative text after a truncated-message reload.static/sessions.js:3864— remove the global "any truncated row" reload condition; scope content recovery to the specific message the caller requested.
The pattern (same as #7040): keep the persisted/canonical path authoritative, never let the bounded preview reach Copy/TTS/export/edit, resolve those by message identity, and cut only at safe structural boundaries. Re-push and I'll re-gate.
Thinking Path
GET /api/sessionresponses already bound oversized hiddenrole:"tool"rows, but equally largeuserorassistantrows still passed through unchanged.Loading conversation...while its WebContent process consumed sustained CPU.type:"text"blocks count toward visible text, while hidden reasoning/tool/raw blocks are never projected into a new preview.What Changed
userandassistantrows in paginated session responses._content_truncatedand_content_original_charsresponse metadata._messages_for_limited_payload(...)chokepoint.msg_limitfull-session retrieval exact and unchanged.limit + 1boundary, source immutability, hidden-block privacy, top-level object semantics, full-load preservation, and edit/regenerate safety.Why It Matters
One oversized historical renderable row could turn an otherwise bounded tail request into a multi-megabyte initial render and permanently stall WKWebView. The guard keeps paginated display payloads renderable while preserving the complete transcript as the source of truth.
Verification
origin/master: the new regressions failed because paginated user/assistant content remained unbounded.origin/master:.venv/bin/python -m py_compile api/routes.py tests/test_session_tail_payload.pypassed.git diff --checkpassed.api/routes.pyfindings are outside this patch.DICT_SECRETprivacy-test sentinel.Loading conversation....Risks / Follow-ups
msg_limitcallers intentionally continue to receive the full transcript.textcould disclose content that was not previously visible.okwith zero active runs and streams. This PR fixes the permanent oversized-row loading failure; it does not claim to fix that residual CPU loop. The open upstream PR fix(perf): virtual measurement retry budget under window oscillation (#6654) #6668 addresses a distinct virtual-measurement retry-loop mechanism and may be relevant to that follow-up: fix(perf): virtual measurement retry budget under window oscillation (#6654) #6668Contract Routing
Task type: focused paginated-session display safety fix.
Touched areas:
api/routes.py: copied response shaping forGET /api/sessionwhenmsg_limitis present.static/sessions.js: authoritative full-history reload when a display row carries_content_truncated.static/ui.js: edit/regenerate actions resolve full text before destructive session operations.tests/test_session_tail_payload.py: observable response, privacy, immutability, and full-load invariants.tests/test_limited_session_content_actions.py: clipped-preview action safety.Relevant public docs:
AGENTS.mdCONTRIBUTING.mddocs/GUIDELINES.mddocs/CONTRACTS.mdScope boundaries:
Evidence needed before claiming done:
Release Note
Prevent oversized user or assistant messages from blocking paginated session rendering while preserving complete session history.
Model Used
AI-assisted with OpenAI GPT-5.6 SOL through Hermes Agent. Notable tools: repository tests and Git inspection, native macOS/WebKit process measurement, live API shape checks without message-body disclosure, and independent fail-closed code review.