fix(state): keep /branch sessions visible after parent reopen
/branch (aka /fork) sessions vanished from /resume and /sessions. Both surfaces funnel through list_sessions_rich(include_children=False), which hid any session with a parent_session_id unless identified as a branch via a heuristic — parent.end_reason == 'branched' AND child.started_at >= parent.ended_at. Two ways that heuristic failed: 1. CLI/gateway branches: once the parent was reopened (e.g. resumed) and re-ended with a different end_reason (tui_shutdown overwriting 'branched'), the heuristic stopped matching and the branch was hidden permanently. 2. TUI branches (tui_gateway session.branch): the TUI never ends the parent as 'branched' — it creates the child while the parent is still live — so the heuristic NEVER matched and TUI branches were hidden from the moment they were created (this is the macOS desktop app's primary symptom). Fix: persist a stable '_branched_from' marker in the branch session's model_config at creation time across ALL THREE branch paths (CLI cli.py, gateway gateway/run.py, and TUI tui_gateway/server.py), and OR a json_extract(model_config, '$._branched_from') IS NOT NULL check into the list_sessions_rich filter. The marker is immutable across the parent's lifecycle, so the branch stays visible regardless of how/whether the parent is ended. The legacy end_reason heuristic is kept (OR'd) so pre-existing branches remain visible. Subagent/compression children (no marker, parent not 'branched') stay correctly hidden. Fixes #20856. Approach by liuhao1024 (PR #20864); reimplemented on current main, extended to the TUI branch path (which the original missed), with regression tests for the reopen+re-end scenario and the TUI marker persistence.
This commit is contained in:
@ -1598,13 +1598,23 @@ class SessionDB:
|
||||
params = []
|
||||
|
||||
if not include_children:
|
||||
# Show root sessions and branch sessions (whose parent ended with
|
||||
# end_reason='branched' before the child was created), while still
|
||||
# hiding sub-agent runs and compression continuations (which also
|
||||
# carry a parent_session_id but were spawned while the parent was
|
||||
# still live — i.e., started_at < parent.ended_at).
|
||||
# Show root sessions and branch sessions, while still hiding
|
||||
# sub-agent runs and compression continuations (which also carry a
|
||||
# parent_session_id but were spawned while the parent was still
|
||||
# live — i.e., started_at < parent.ended_at).
|
||||
#
|
||||
# Branch sessions are identified two ways, OR'd for robustness:
|
||||
# 1. A stable ``_branched_from`` marker in model_config, written
|
||||
# by /branch at creation time. This survives the parent being
|
||||
# reopened and re-ended with a different end_reason (e.g.
|
||||
# tui_shutdown overwriting 'branched'), which otherwise hides
|
||||
# the branch — see issue #20856.
|
||||
# 2. The legacy heuristic (parent ended with 'branched' before the
|
||||
# child started), covering branch sessions created before the
|
||||
# marker existed.
|
||||
where_clauses.append(
|
||||
"(s.parent_session_id IS NULL"
|
||||
" OR json_extract(s.model_config, '$._branched_from') IS NOT NULL"
|
||||
" OR EXISTS (SELECT 1 FROM sessions p"
|
||||
" WHERE p.id = s.parent_session_id"
|
||||
" AND p.end_reason = 'branched'"
|
||||
|
||||
Reference in New Issue
Block a user