fix(session): keep bounded-tail recovery suffix and paging offset

Bounded settle/cancel reloads return the latest 30 messages, not a
prefix of the visible window. The shorter-terminal preserve path only
compared prefixes, so a long session dropped the Connection-interrupted
marker. Anchor persist also ran before _oldestIdx was refreshed from
the bounded response, writing the scene against the wrong transcript
row.

- Treat a suffix match like a prefix match when preserving the terminal
  recovery marker
- Refresh _messagesTruncated / _oldestIdx before attaching/persisting
  the projected scene on settle, cancel, and apperror
- Document the msg_limit=30 / msg_limit=all contract
- Add runtime + source-order regression tests
This commit is contained in:
happy5318 committed 2026-09-18 22:00:45 +08:00
1 parent 39643e748f
commit f4b28346e5
5 files changed
+224 -21

No files matched your search

+11 -2
View File
@@ -511,7 +511,9 @@ to the active conversation rather than a global app setting.
Session management:
newSession() POST /api/session/new, update S.session, save to localStorage
loadSession(sid) GET /api/session?session_id=X, check INFLIGHT first, update S
loadSession(sid) GET /api/session?session_id=X (initial load uses the
bounded tail `msg_limit=30`; jump-to-start and outline
jump pass `msg_limit=all`), check INFLIGHT first, update S
deleteSession(sid) POST /api/session/delete, handle active/inactive cases correctly
renderSessionList() GET /api/sessions, rebuild #sessionList DOM
@@ -1379,7 +1381,14 @@ Complete list of all HTTP endpoints as of Sprint 1 (v0.3).
/ Returns full HTML app (index page)
/index.html Same as /
/health {"status":"ok","sessions":N}
/api/session ?session_id=X -> full session + messages. 400 if no ID.
/api/session ?session_id=X -> session + messages. 400 if no ID.
Bare (no msg_limit) keeps the historical full-transcript
contract. Recovery paths request a bounded tail
(`msg_limit=30`) and restore `_messages_truncated` /
`_messages_offset` before persisting anchor-scene
metadata. Outline jump and jump-to-start opt in to the
full transcript via the explicit `msg_limit=all`
escape hatch. See #7310 / #7625 / #7628.
/api/sessions List of all session compact() dicts, sorted by updated_at
/api/list ?session_id=X&path=. -> directory listing for session workspace
/api/file ?session_id=X&path=rel -> file content (text, 200KB limit)