Merge pull request #5356 from nesquena/stage-5308
Release — subagent sessions no longer vanish from sidebar (#5308)
This commit is contained in:
3 files changed
+98
No files matched your search
@@ -9,6 +9,8 @@ _No unreleased changes. Entries are moved into their version block when a releas
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Delegated subagent sessions no longer vanish from the sidebar.** A subagent (delegate_task) child whose sidebar row was built from a stale sidecar reporting 0 messages now receives its real message count from the agent state.db, so it stays visible (nested under its parent) instead of being silently dropped by the sidebar visibility filter. (#5308)
|
||||
|
||||
- **No more scroll jump-back on mobile while messages load.** When the app realigns the viewport after loading older messages, mobile browsers no longer double-compensate the scroll position (their native overflow-anchor fighting the app's own scroll write), which caused a visible jump. Desktop behavior is unchanged. (#5338)
|
||||
|
||||
- **No more white flicker/flash while the assistant streams on light themes.** Live token-by-token markdown updates no longer inherit the global dark/light `color`/`background` transition, so the streaming turn paints instantly instead of briefly fading on each token. Theme-switch transitions elsewhere are unchanged. (#5328)
|
||||
|
||||
@@ -4028,6 +4028,20 @@ def _apply_sidebar_state_db_override_metadata(sessions: list[dict], metadata: di
|
||||
session['session_source'] = state_db_session_source
|
||||
session['source_label'] = state_db_source_label
|
||||
session['is_cli_session'] = False
|
||||
# Overlay the real state.db message count for WebUI-owned rows AND for
|
||||
# delegated subagent children (#5308). A subagent child
|
||||
# (state_db_source == 'subagent') is backed by the delegate runner's
|
||||
# state.db session, but its sidebar row is built from a stale sidecar
|
||||
# that often reports message_count == 0. Without overlaying the true
|
||||
# count, the front-end visibility predicate
|
||||
# (_sidebarRowHasVisibleMessages) drops the row and the subagent
|
||||
# session vanishes from the sidebar entirely (regression seam behind
|
||||
# #5308, same state.db-blind-metadata root as the #5307 transcript
|
||||
# recovery). The count overlay keeps the same conservative
|
||||
# anti-resurrection guard used for WebUI rows. The source-tag / title
|
||||
# reassignment above stays WebUI-only — a subagent child keeps its
|
||||
# subagent classification.
|
||||
if state_db_source in ('webui', 'subagent'):
|
||||
try:
|
||||
current_count = max(0, int(session.get('message_count') or 0))
|
||||
state_count = max(0, int(state_db_message_count or 0))
|
||||
|
||||
@@ -155,6 +155,88 @@ def test_sidebar_state_db_overlay_preserves_numeric_actual_count():
|
||||
assert sessions[0]["actual_message_count"] == 5
|
||||
|
||||
|
||||
def test_sidebar_state_db_overlay_counts_subagent_child_5308():
|
||||
"""#5308: a delegated subagent child whose stale sidecar reports
|
||||
message_count == 0 must receive its real state.db message count so the
|
||||
front-end sidebar visibility predicate does not drop the row (the subagent
|
||||
session vanishing regression). The overlay applies to source == 'subagent'
|
||||
just like 'webui', while the subagent source classification is preserved.
|
||||
"""
|
||||
import api.models as models
|
||||
|
||||
sid = "subagent_child_5308"
|
||||
sessions = [
|
||||
{
|
||||
"session_id": sid,
|
||||
"source_tag": "subagent",
|
||||
"raw_source": "subagent",
|
||||
"session_source": "other",
|
||||
"relationship_type": "child_session",
|
||||
"parent_session_id": "parent_abc",
|
||||
"message_count": 0,
|
||||
"actual_message_count": 0,
|
||||
"last_message_at": 0,
|
||||
"updated_at": 0,
|
||||
}
|
||||
]
|
||||
|
||||
models._apply_sidebar_state_db_override_metadata(
|
||||
sessions,
|
||||
{
|
||||
sid: {
|
||||
"_state_db_source": "subagent",
|
||||
"_state_db_message_count": 6,
|
||||
"_state_db_last_message_at": 2002.0,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Real state.db count is now overlaid (was 0) -> row survives the sidebar
|
||||
# visibility predicate instead of vanishing.
|
||||
assert sessions[0]["message_count"] == 6
|
||||
assert sessions[0]["actual_message_count"] == 6
|
||||
assert sessions[0]["last_message_at"] == 2002.0
|
||||
# Subagent classification is preserved (source-tag reassignment stays
|
||||
# WebUI-only) — the child does not get re-tagged as a webui session.
|
||||
assert sessions[0]["source_tag"] == "subagent"
|
||||
assert sessions[0].get("is_cli_session") is not False
|
||||
|
||||
|
||||
def test_sidebar_state_db_overlay_does_not_count_foreign_cli_source_5308():
|
||||
"""Guard the #5308 overlay scope: a non-webui, non-subagent foreign source
|
||||
(e.g. a messaging/cron/tui CLI row) must NOT get the count overlay — only
|
||||
WebUI-owned rows and delegated subagent children do.
|
||||
"""
|
||||
import api.models as models
|
||||
|
||||
sid = "cron_row_5308"
|
||||
sessions = [
|
||||
{
|
||||
"session_id": sid,
|
||||
"source_tag": "cron",
|
||||
"message_count": 0,
|
||||
"actual_message_count": 0,
|
||||
"last_message_at": 0,
|
||||
"updated_at": 0,
|
||||
}
|
||||
]
|
||||
|
||||
models._apply_sidebar_state_db_override_metadata(
|
||||
sessions,
|
||||
{
|
||||
sid: {
|
||||
"_state_db_source": "cron",
|
||||
"_state_db_message_count": 9,
|
||||
"_state_db_last_message_at": 3003.0,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# cron is neither webui nor subagent -> count overlay must NOT apply.
|
||||
assert sessions[0]["message_count"] == 0
|
||||
assert sessions[0]["actual_message_count"] == 0
|
||||
|
||||
|
||||
def test_tail_cancelled_partial_blocks_state_db_replay():
|
||||
from api.models import merge_session_messages_append_only
|
||||
|
||||
|
||||
Reference in new issue
Block a user