@@ -346,7 +346,7 @@ class _LiteSessionFile:
346346
347347 __slots__ = ("mtime" , "size" , "head" , "tail" )
348348
349- def __init__ (self , mtime : int , size : int , head : str , tail : str ) -> None :
349+ def __init__ (self , mtime : int , size : int | None , head : str , tail : str ) -> None :
350350 self .mtime = mtime
351351 self .size = size
352352 self .head = head
@@ -1506,16 +1506,18 @@ def _filter_transcript_entries(entries: list[Any]) -> list[_TranscriptEntry]:
15061506 return result
15071507
15081508
1509- async def _load_store_entries_as_jsonl (
1509+ async def _load_store_session_lite (
15101510 store : SessionStore , session_id : str , directory : str | None
1511- ) -> str | None :
1512- """Load entries from a SessionStore and serialize to a JSONL string .
1511+ ) -> _LiteSessionFile | None :
1512+ """Load a session from a SessionStore as a ``_LiteSessionFile`` .
15131513
15141514 Only the head/tail slice needed for lite-parse summary derivation is
15151515 fetched when the store implements :meth:`SessionStore.load_range`;
1516- otherwise falls back to a full :meth:`SessionStore.load`. Either way the
1517- result is fed to ``_jsonl_to_lite`` which itself slices to a 64KB
1518- head/tail, so the parse path is identical.
1516+ otherwise falls back to a full :meth:`SessionStore.load` and slices via
1517+ ``_jsonl_to_lite``. Head and tail are serialized separately on the
1518+ ``load_range`` path so head-only scans (e.g. ``first_prompt``) never see
1519+ tail entries; ``size`` is ``None`` on that path since only a slice was
1520+ fetched.
15191521
15201522 Returns ``None`` if the session has no entries.
15211523 """
@@ -1525,12 +1527,24 @@ async def _load_store_entries_as_jsonl(
15251527 head , tail = await store .load_range (
15261528 key , head = _LITE_LOAD_HEAD_ENTRIES , tail = _LITE_LOAD_TAIL_ENTRIES
15271529 ) or ([], [])
1528- entries_for_lite : list [Any ] | None = list (head ) + list (tail )
1529- else :
1530- entries_for_lite = await store .load (key )
1531- if not entries_for_lite :
1530+ if not head and not tail :
1531+ return None
1532+ head_jsonl = _entries_to_jsonl (list (head )) if head else ""
1533+ # Mirror the disk path's "tail = head when small" semantics so
1534+ # adapters that return an empty tail for short sessions still expose
1535+ # the head entries to tail-scanning extractors.
1536+ tail_jsonl = _entries_to_jsonl (list (tail )) if tail else head_jsonl
1537+ return _LiteSessionFile (
1538+ mtime = _mtime_from_jsonl_tail (tail_jsonl or head_jsonl ),
1539+ size = None ,
1540+ head = head_jsonl ,
1541+ tail = tail_jsonl ,
1542+ )
1543+ entries = await store .load (key )
1544+ if not entries :
15321545 return None
1533- return _entries_to_jsonl (entries_for_lite )
1546+ jsonl = _entries_to_jsonl (entries )
1547+ return _jsonl_to_lite (jsonl , _mtime_from_jsonl_tail (jsonl ))
15341548
15351549
15361550async def list_sessions_from_store (
@@ -1595,9 +1609,9 @@ async def list_sessions_from_store(
15951609 # disk path.
15961610 sem = asyncio .Semaphore (_STORE_LIST_LOAD_CONCURRENCY )
15971611
1598- async def _bounded_load (sid : str ) -> str | None :
1612+ async def _bounded_load (sid : str ) -> _LiteSessionFile | None :
15991613 async with sem :
1600- return await _load_store_entries_as_jsonl (session_store , sid , directory )
1614+ return await _load_store_session_lite (session_store , sid , directory )
16011615
16021616 settled = await asyncio .gather (
16031617 * (_bounded_load (e ["session_id" ]) for e in listing ),
@@ -1614,9 +1628,7 @@ async def _bounded_load(sid: str) -> str | None:
16141628 continue
16151629 if outcome is None :
16161630 continue
1617- parsed = _parse_session_info_from_lite (
1618- sid , _jsonl_to_lite (outcome , mtime ), project_path
1619- )
1631+ parsed = _parse_session_info_from_lite (sid , outcome , project_path )
16201632 if parsed is None :
16211633 # Sidechain or no extractable summary — drop, matching the
16221634 # filesystem path.
@@ -1648,10 +1660,9 @@ async def get_session_info_from_store(
16481660 """
16491661 if not _validate_uuid (session_id ):
16501662 return None
1651- jsonl = await _load_store_entries_as_jsonl (session_store , session_id , directory )
1652- if jsonl is None :
1663+ lite = await _load_store_session_lite (session_store , session_id , directory )
1664+ if lite is None :
16531665 return None
1654- lite = _jsonl_to_lite (jsonl , _mtime_from_jsonl_tail (jsonl ))
16551666 project_path = _canonicalize_path (str (directory ) if directory is not None else "." )
16561667 return _parse_session_info_from_lite (session_id , lite , project_path )
16571668
0 commit comments