fix: keep cross-surface session continuations visible

This commit is contained in:
ai-ag2026
2026-05-07 10:26:01 +02:00
committed by nesquena-hermes
parent f77b8aad5b
commit 7d5704c3bc
4 changed files with 89 additions and 3 deletions
+4
View File
@@ -527,6 +527,10 @@ def read_session_lineage_metadata(db_path: Path, session_ids: list[str] | set[st
entry['relationship_type'] = 'child_session'
entry['parent_title'] = parent_row.get('title')
entry['parent_source'] = parent_row.get('source')
parent_source = str(parent_row.get('source') or '').strip().lower()
child_source = str(row.get('source') or '').strip().lower()
if parent_source and child_source and parent_source != child_source:
entry['_cross_surface_child_session'] = True
parent_root = _continuation_root_id(rows, parent_id)
if parent_root:
entry['_parent_lineage_root_id'] = parent_root
+4
View File
@@ -1870,6 +1870,10 @@ function _attachChildSessionsToSidebarRows(collapsedRows, rawSessions){
const orphans=[];
for(const child of rawSessions||[]){
if(!_isChildSession(child)) continue;
if(child._cross_surface_child_session){
orphans.push({...child,_orphan_child_session:true});
continue;
}
const parentSid=child.parent_session_id;
let parentRow=visibleBySid.get(parentSid);
let parentSegment=null;
+46
View File
@@ -126,6 +126,8 @@ console.log(JSON.stringify({{sid: collapsed[0].session_id, containsRoot: _sessio
assert '"containsRoot":true' in result
def test_sidebar_attaches_child_sessions_to_collapsed_hidden_parent_lineage():
js = SESSIONS_JS_PATH.read_text(encoding="utf-8")
source = f"""
@@ -162,3 +164,47 @@ console.log(JSON.stringify(attached));
assert [row["session_id"] for row in rows] == ["tip"]
assert rows[0]["_child_session_count"] == 1
assert rows[0]["_child_sessions"][0]["session_id"] == "child"
def test_cross_surface_webui_child_session_remains_top_level_when_parent_is_messaging():
js = SESSIONS_JS_PATH.read_text(encoding="utf-8")
source = f"""
const src = {js!r};
function extractFunc(name) {{
const re = new RegExp('function\\\\s+' + name + '\\\\s*\\\\(');
const start = src.search(re);
if (start < 0) throw new Error(name + ' not found');
let i = src.indexOf('{{', start);
let depth = 1; i++;
while (depth > 0 && i < src.length) {{
if (src[i] === '{{') depth++;
else if (src[i] === '}}') depth--;
i++;
}}
return src.slice(start, i);
}}
eval(extractFunc('_isChildSession'));
eval(extractFunc('_sidebarLineageKeyForRow'));
eval(extractFunc('_attachChildSessionsToSidebarRows'));
const collapsed = [{{session_id:'telegram_parent', title:'Telegram parent', source_label:'Telegram'}}];
const raw = [
collapsed[0],
{{
session_id:'webui_tip',
title:'Current WebUI continuation',
parent_session_id:'telegram_parent',
relationship_type:'child_session',
parent_source:'telegram',
source_label:'Telegram',
session_source:'messaging',
raw_source:'telegram',
_cross_surface_child_session:true,
}},
];
const rows = _attachChildSessionsToSidebarRows(collapsed, raw);
console.log(JSON.stringify(rows));
"""
rows = json.loads(_run_node(source))
assert [row["session_id"] for row in rows] == ["telegram_parent", "webui_tip"]
assert rows[1].get("_orphan_child_session") is True
assert "_child_sessions" not in rows[0]
+35 -3
View File
@@ -45,14 +45,14 @@ def _ensure_state_db(path):
return conn
def _insert_state_row(conn, sid, *, parent=None, ended_at=None, end_reason=None, started_at=None):
def _insert_state_row(conn, sid, *, parent=None, ended_at=None, end_reason=None, started_at=None, source='webui'):
conn.execute(
"""
INSERT INTO sessions
(id, source, title, model, started_at, message_count, parent_session_id, ended_at, end_reason)
VALUES (?, 'webui', ?, 'openai/gpt-5', ?, 2, ?, ?, ?)
VALUES (?, ?, ?, 'openai/gpt-5', ?, 2, ?, ?, ?)
""",
(sid, sid, started_at or time.time(), parent, ended_at, end_reason),
(sid, source, sid, started_at or time.time(), parent, ended_at, end_reason),
)
conn.commit()
@@ -202,3 +202,35 @@ def test_cli_close_parent_preserves_cross_surface_continuation_lineage(_isolate)
assert rows["lineage_api_webui_child"].get("_lineage_root_id") == "lineage_api_cli_parent"
finally:
conn.close()
def test_cross_surface_child_session_metadata_marks_orphan_top_level_candidate(_isolate):
conn = _ensure_state_db(_isolate)
t0 = time.time() - 100
try:
_save_webui_session("lineage_api_telegram_parent", title="Telegram parent", updated_at=t0)
_save_webui_session("lineage_api_webui_tip", title="WebUI tip", updated_at=t0 + 10)
_insert_state_row(
conn,
"lineage_api_telegram_parent",
source="telegram",
started_at=t0,
ended_at=t0 + 5,
end_reason="compression",
)
_insert_state_row(
conn,
"lineage_api_webui_tip",
source="webui",
parent="lineage_api_telegram_parent",
started_at=t0 + 6,
)
rows = {row["session_id"]: row for row in all_sessions()}
tip = rows["lineage_api_webui_tip"]
assert tip.get("relationship_type") == "child_session"
assert tip.get("parent_source") == "telegram"
assert tip.get("_cross_surface_child_session") is True
finally:
conn.close()