fix: keep cross-source continuations separate in sidebar
This commit is contained in:
1 parent
9986d2fd30
commit
3c93d5a702
2 files changed
+68
-1
No files matched your search
+13
-1
@@ -79,9 +79,18 @@ def _is_continuation_session(parent: dict | None, child: dict | None) -> bool:
|
||||
should continue the same visible conversation rather than becoming a
|
||||
separate child-session row. Plain parent/child links that started before the
|
||||
parent's ended boundary remain child sessions.
|
||||
|
||||
Do not collapse lineage across raw sources. A WebUI session that continues
|
||||
from a Telegram/CLI/etc. parent must remain visible as its own surface-owned
|
||||
conversation; otherwise the tip inherits the root's title/source metadata and
|
||||
can disappear under messaging/sidebar policies.
|
||||
"""
|
||||
if not parent or not child:
|
||||
return False
|
||||
parent_source = str(parent.get('source') or '').strip().lower()
|
||||
child_source = str(child.get('source') or '').strip().lower()
|
||||
if parent_source and child_source and parent_source != child_source:
|
||||
return False
|
||||
if parent.get('end_reason') not in {'compression', 'cli_close'}:
|
||||
return False
|
||||
ended_at = parent.get('ended_at')
|
||||
@@ -133,10 +142,13 @@ def _project_agent_session_rows(rows: list[dict]) -> list[dict]:
|
||||
if not parent_id:
|
||||
continue
|
||||
children_by_parent.setdefault(parent_id, []).append(row)
|
||||
if _is_continuation_session(rows_by_id.get(parent_id), row):
|
||||
parent = rows_by_id.get(parent_id)
|
||||
if _is_continuation_session(parent, row):
|
||||
continuation_child_ids.add(row['id'])
|
||||
else:
|
||||
row['relationship_type'] = 'child_session'
|
||||
row['parent_title'] = parent.get('title') if parent else None
|
||||
row['parent_source'] = parent.get('source') if parent else None
|
||||
parent_root = _continuation_root_id(rows_by_id, parent_id)
|
||||
if parent_root:
|
||||
row['_parent_lineage_root_id'] = parent_root
|
||||
|
||||
@@ -773,6 +773,61 @@ def test_agent_session_source_normalization_contract():
|
||||
assert normalized['raw_source'] is None
|
||||
|
||||
|
||||
def test_cross_source_parent_child_is_not_collapsed_into_root_metadata(cleanup_test_sessions):
|
||||
"""A WebUI continuation from a messaging parent must keep WebUI metadata.
|
||||
|
||||
Regression for a production case where a WebUI session continued from a
|
||||
Telegram compression chain and was projected as the old Telegram root,
|
||||
inheriting the wrong title/source and hiding from the expected sidebar view.
|
||||
"""
|
||||
from api.agent_sessions import read_importable_agent_session_rows
|
||||
|
||||
conn = _ensure_state_db()
|
||||
root_sid = 'gw_tg_cross_source_root_001'
|
||||
webui_sid = 'webui_cross_source_tip_001'
|
||||
now = time.time()
|
||||
cleanup_test_sessions.extend([root_sid, webui_sid])
|
||||
try:
|
||||
_insert_agent_session_row(
|
||||
conn,
|
||||
session_id=root_sid,
|
||||
source='telegram',
|
||||
title='Old Telegram Root',
|
||||
started_at=now - 20,
|
||||
ended_at=now - 10,
|
||||
end_reason='compression',
|
||||
messages=2,
|
||||
)
|
||||
_insert_agent_session_row(
|
||||
conn,
|
||||
session_id=webui_sid,
|
||||
source='webui',
|
||||
title='Current WebUI Work',
|
||||
started_at=now - 9,
|
||||
parent_session_id=root_sid,
|
||||
messages=2,
|
||||
)
|
||||
|
||||
rows = read_importable_agent_session_rows(_get_state_db_path(), exclude_sources=None)
|
||||
by_id = {row['id']: row for row in rows}
|
||||
|
||||
assert webui_sid in by_id
|
||||
assert root_sid in by_id
|
||||
webui = by_id[webui_sid]
|
||||
assert webui.get('title') == 'Current WebUI Work'
|
||||
assert webui.get('source') == 'webui'
|
||||
assert webui.get('session_source') == 'webui'
|
||||
assert webui.get('source_label') == 'WebUI'
|
||||
assert webui.get('relationship_type') == 'child_session'
|
||||
assert webui.get('parent_title') == 'Old Telegram Root'
|
||||
finally:
|
||||
try:
|
||||
_remove_test_sessions(conn, root_sid, webui_sid)
|
||||
conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def test_gateway_watcher_uses_normalized_source_metadata(monkeypatch):
|
||||
"""SSE snapshots use the same normalized source contract as /api/sessions."""
|
||||
conn = _ensure_state_db()
|
||||
|
||||
Reference in new issue
Block a user