feat(chat): hide [[SILENT]] assistant turns (stacked on #6420) - #1
Open
ruizanthony wants to merge 11 commits into
Open
ruizanthony wants to merge 11 commits into
ruizanthony wants to merge 11 commits into
Conversation
…ntract 11 Node-harness cases covering: full silent-turn collapse (plain and with tool calls), concluding turns staying visible, mixed consecutive wakeups, lone sentinel replies, sentinel embedded in real text, whitespace-trimmed sentinel, hidden turns remaining true turn boundaries (finals/compact-worklog semantics), live-stream prefix suppression semantics, and the CSS/token wiring contract.
Background wakeups (notify_on_complete) each trigger a full agent turn whose reply renders as a normal message. When several processes finish in sequence, the intermediate acknowledgements pollute the thread between the user's messages and the one conclusion that matters. Contract (render-only, persisted data never mutated): - an assistant reply whose trimmed content is exactly [[SILENT]] is hidden; - when a process_wakeup turn ends with the sentinel, the whole turn (wakeup row + assistant replies incl. tool-carrying ones) collapses; - hidden rows remain true turn boundaries via the generalized _hasHiddenProcessWakeupBoundaryBefore (preserves nesquena#6420 semantics when no sentinel is involved); - while streaming, the live bubble hides as soon as the accumulated text is a prefix of the sentinel (data-silent-pending + CSS), driven from the per-token/interim chokepoint syncInflightAssistantMessage(); - safety net: a reply that does not use the exact sentinel stays visible. No new setting: the filter is unconditional and agent-agnostic; the agent opts in per-turn by answering exactly [[SILENT]].
ruizanthony
force-pushed
the
feat/configurable-background-wakeups
branch
7 times, most recently
from
August 15, 2026 01:13
5454b41 to
a0bdad2
Compare
ruizanthony
force-pushed
the
feat/configurable-background-wakeups
branch
3 times, most recently
from
August 24, 2026 23:57
f7ca618 to
6898518
Compare
ruizanthony
force-pushed
the
feat/configurable-background-wakeups
branch
from
August 29, 2026 02:21
6898518 to
77a8927
Compare
ruizanthony
added a commit
that referenced
this pull request
Aug 29, 2026
Replay repair runs on every session LOAD, not just on save, and it dominates cold-load cost. It re-serializes every assistant message to canonical JSON and SHA-256s it, for the session AND for every compaction ancestor walked by the lineage — so a chat with 4 ancestors pays it five times per tab refresh. A py-spy profile taken under 6 concurrent loads on a live deployment put `_canonical_message_digest` as the #1 self-time frame. Because the work happens under the GIL it does not overlap between tabs: one load took 0.55s while six concurrent loads took 10.83s for the slowest (19.6x). The work is also almost always for nothing. Sampling the 60 most recent sidecars, repair changed nothing in 60 of 60 cases — 11.8s of pure waste. A file only needs repairing once; a clean file stays clean until it is rewritten. Memoize the NEGATIVE verdict only, keyed by the sha256 of the exact file bytes (already computed by the primary load path for revision tracking). A hit means "these exact bytes were proven to need no repair" and skips the pipeline. Any write changes the digest and therefore misses the cache. Measured on 6 real sidecars, same machine, warm: single load (median) 136ms -> 46ms (2.9x) single load (6 sessions) 875ms -> 302ms (2.9x) 6 concurrent loads (wall) 1054ms -> 279ms (3.8x) 6 concurrent loads (slowest) 1053ms -> 277ms (3.8x) Safety. Skipping repair is equivalent to running it because the collapse helpers never mutate their input: they build new lists and return (result, changed), returning the input untouched when changed is False. A test pins that property, so a future helper that starts mutating in place fails the suite instead of silently serving unrepaired sessions. A positive verdict is deliberately not cached: a file needing repair gets rewritten, so caching it would key on bytes that no longer exist. The cache is bounded (512 entries, LRU) and a missing digest forces the full pipeline.
ruizanthony
added a commit
that referenced
this pull request
Aug 30, 2026
Replay repair runs on every session LOAD, not just on save, and it dominates cold-load cost. It re-serializes every assistant message to canonical JSON and SHA-256s it, for the session AND for every compaction ancestor walked by the lineage — so a chat with 4 ancestors pays it five times per tab refresh. A py-spy profile taken under 6 concurrent loads on a live deployment put `_canonical_message_digest` as the #1 self-time frame. Because the work happens under the GIL it does not overlap between tabs: one load took 0.55s while six concurrent loads took 10.83s for the slowest (19.6x). The work is also almost always for nothing. Sampling the 60 most recent sidecars, repair changed nothing in 60 of 60 cases — 11.8s of pure waste. A file only needs repairing once; a clean file stays clean until it is rewritten. Memoize the NEGATIVE verdict only, keyed by the sha256 of the exact file bytes (already computed by the primary load path for revision tracking). A hit means "these exact bytes were proven to need no repair" and skips the pipeline. Any write changes the digest and therefore misses the cache. Measured on 6 real sidecars, same machine, warm: single load (median) 136ms -> 46ms (2.9x) single load (6 sessions) 875ms -> 302ms (2.9x) 6 concurrent loads (wall) 1054ms -> 279ms (3.8x) 6 concurrent loads (slowest) 1053ms -> 277ms (3.8x) Safety. Skipping repair is equivalent to running it because the collapse helpers never mutate their input: they build new lists and return (result, changed), returning the input untouched when changed is False. A test pins that property, so a future helper that starts mutating in place fails the suite instead of silently serving unrepaired sessions. A positive verdict is deliberately not cached: a file needing repair gets rewritten, so caching it would key on bytes that no longer exist. The cache is bounded (512 entries, LRU) and a missing digest forces the full pipeline.
ruizanthony
added a commit
that referenced
this pull request
Sep 2, 2026
Replay repair runs on every session LOAD, not just on save, and it dominates cold-load cost. It re-serializes every assistant message to canonical JSON and SHA-256s it, for the session AND for every compaction ancestor walked by the lineage — so a chat with 4 ancestors pays it five times per tab refresh. A py-spy profile taken under 6 concurrent loads on a live deployment put `_canonical_message_digest` as the #1 self-time frame. Because the work happens under the GIL it does not overlap between tabs: one load took 0.55s while six concurrent loads took 10.83s for the slowest (19.6x). The work is also almost always for nothing. Sampling the 60 most recent sidecars, repair changed nothing in 60 of 60 cases — 11.8s of pure waste. A file only needs repairing once; a clean file stays clean until it is rewritten. Memoize the NEGATIVE verdict only, keyed by the sha256 of the exact file bytes (already computed by the primary load path for revision tracking). A hit means "these exact bytes were proven to need no repair" and skips the pipeline. Any write changes the digest and therefore misses the cache. Measured on 6 real sidecars, same machine, warm: single load (median) 136ms -> 46ms (2.9x) single load (6 sessions) 875ms -> 302ms (2.9x) 6 concurrent loads (wall) 1054ms -> 279ms (3.8x) 6 concurrent loads (slowest) 1053ms -> 277ms (3.8x) Safety. Skipping repair is equivalent to running it because the collapse helpers never mutate their input: they build new lists and return (result, changed), returning the input untouched when changed is False. A test pins that property, so a future helper that starts mutating in place fails the suite instead of silently serving unrepaired sessions. A positive verdict is deliberately not cached: a file needing repair gets rewritten, so caching it would key on bytes that no longer exist. The cache is bounded (512 entries, LRU) and a missing digest forces the full pipeline.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Stacked on nesquena#6420 — will be retargeted to upstream
mainafter nesquena#6420 merges.Each background-process wakeup (
notify_on_complete) starts a full agent turn whose reply renders as a normal message. With several processes finishing in sequence, the intermediate acknowledgements pollute the thread between the user's messages and the one conclusion that matters (measured on real sessions: 85 wakeups → 85 visible intermediate replies across 40 recent sessions).Contract (render-only — persisted data never mutated)
[[SILENT]]is not rendered.process_wakeupturn ends with the sentinel, the whole turn collapses (wakeup row + every assistant reply, including tool-carrying ones)._hasHiddenProcessWakeupBoundaryBefore; feat(wakeup): configure visibility and route compressed origins nesquena/hermes-webui#6420 semantics preserved when no sentinel is involved) — Compact Worklog finals, question mapping, regenerate ownership unaffected.data-silent-pending+ CSS), driven from the per-token/interim chokepointsyncInflightAssistantMessage(); the exact sentinel never flashes at stream end.No new setting: the filter is unconditional and agent-agnostic; the agent opts in per turn by answering exactly
[[SILENT]].Tests
tests/test_silent_wakeup_turns.py: 11 Node-harness cases (full collapse, tool-carrying turns, concluding turns visible, mixed wakeups, lone sentinels, embedded sentinel, whitespace trimming, boundary/finals semantics, live prefix suppression, CSS+wiring presence).tests/test_process_wakeup_rendering.py: harness updated to eval the new helpers (same pattern as other extract-based drivers).node --checkclean; ESLint runtime gate clean;scope_undef_gateclean.Out of scope
Telegram/gateway surfaces (separate PR if wanted).